Co oznacza ten dziwny symbol „:>” w bash

47

Znalazłem coś w skrypcie, ale nie należę do skryptu głównego. Był :>w kolejce.

Czy możesz mi wyjaśnić, co to znaczy?

:> file
while read A B C D E; do echo "$A;$B;$D;$E;$C" >> file; done < otherfile
diego9403
źródło
6
Co ważne, :>nie jest to pojedynczy operator. Łatwiej zrozumieć, jeśli : > filezamiast tego czytasz .
jpfx1342,
Oznacza to, że osoba, pisząc scenariusz powinien przekierowany wyjście z pętli do pliku: while read A B C D E; do echo "$A;$B;$D;$E;$C"; done < otherfile > file. Lub jeszcze lepiej, powinni byli użyć odpowiedniego narzędzia do pracy, awk, jak sugeruje Peter . Nawiasem mówiąc, prawie zawsze chcesz używać -rprzełącznika zread .
Tom Fenech
Poza uderzeniem byłaby to buźka dla wrony.
smci

Odpowiedzi:

46

Było:> w wierszu skryptu bash. Co to znaczy?

:> file

Jest to skrótowy sposób powiedzenia:

  • Jeśli filenie istnieje, utwórz go, skróć go do 0bajtów.

Oznacza to, że możesz być pewien, że fileistnieje i jest pusty.

Możesz także użyć, > fileale :> filejest bardziej przenośny.

Zobacz pytanie Przepełnienie stosu Jaki jest cel wbudowanego GNU Bash „:” (dwukropka)? po więcej informacji.

DavidPostill
źródło
Nie rozumiem drugiej linii. Myślałem, że czytają zmienne. Echo poleceń jest również dziwne. Czy możesz wytłumaczyć?
diego9403,
Nie jestem ekspertem, ale myślę, Unix druga linia czyta rzeczy z otherfilei echoy im się file. Dokonuje również zmiennych na podstawie tego, co czyta ... Jeśli chcesz uzyskać jednoznaczną odpowiedź, zadaj własne pytanie.
DavidPostill
2
@ diego9403: readpobiera dane wejściowe ze standardowego wejścia. Samodzielnie czytałby to, co piszesz. Ponieważ stdin zostało przekierowane, <otherfilezawartość otherfile„jest wpisywana” na stdin. Więc readprzenosi wartości linia po linii do zmiennych $ A, $ B, $ C, $ D i $ E.
slebetman
Czyli jest to bardziej niejasna alternatywa dla truncateCoreutils?
Federico Poloni,
1
@PeterCordes Nie miałem na myśli „niejasnego”, jak w „rzadkościach”, ale jak w „mniej czytelnych dla czytelnika”.
Federico Poloni,
29

Wygląda na fantazyjny sposób tworzenia nowego pliku. In bash :jest poleceniem zerowym:

$ type : 
: is a shell builtin 
$ help : 
:: :
    Null command.

    No effect; the command does nothing.

    Exit Status:
    Always succeeds.

>przekierowuje wyjście :do pliku.

Arkadiusz Drabczyk
źródło
2
Obetnie
2
tak, właśnie to >robi
Arkadiusz Drabczyk
2
:jest skrótem od true. Możliwe, że w niektórych powłokach truenie jest wbudowany? Oba są wbudowane w bash.
Peter Cordes,
12

:to inna nazwa dla true. Oba są wbudowanymi powłokami w bash, ale nie ma /bin/:, tylko /bin/true. Przekierowanie danych wyjściowych powoduje powłokę do open(2)pliku za pomocą O_CREAT|O_TRUNC. Jeśli nic nie jest zapisane, pozostaje na zerowej długości.

Złożenie tych dwóch części razem :> filejest dość powszechnym idiomem obcinania plików. Większość ludzi starałaby się jednak robić mniej dziwnie, pisząc : >file.


Ponieważ zapytałeś w komentarzu do drugiej linii, zamienię moje komentarze w odpowiedź. (mimo że nie zadałeś tego w swoim pytaniu).

Drugi wiersz to pętla, która odczytuje wiersze z otherfileniektórych nazwanych zmiennych. Ciało pętli używa echodo drukowania ich za pomocą ;separatorów zamiast jakichkolwiek białych znaków, które mieli wcześniej. filejest zamykany i ponownie otwierany (w celu dołączenia) każdej iteracji, ponieważ przekierowanie znajduje się w pętli. Używanie while ...;do read -r ...;done <otherfile >filezmniejszyłoby ssanie i uniknęło konieczności obcięcia pliku w pierwszej kolejności. read -rnie je \jako postać ucieczki.

Przetwarzanie tekstu w bash jest dość powolne. Część tego jest nieunikniona: readmusi przejść jeden bajt na raz (jedno read(2)wywołanie systemowe na bajt), aby uniknąć przekroczenia końca linii. Lepiej byłoby użyć odpowiedniego narzędzia do pracy:

awk -vOFS=';' '{ print $1, $2, $4, $5, $3 }' -- otherfile  >file

--oznacza, że ​​twój skrypt się nie psuje, jeśli otherfilenazywa się coś głupiego jak --version.

Ustawienie Separatora pól wyjściowych ;oznacza, że ​​możesz po prostu przekazać wiele pól jako argumenty do wydrukowania. Shell readprzypisuje całą resztę linii spacją do ostatniej zmiennej, ale nie ma sposobu, aby powiedzieć awk, aby dzielił się tylko na 5. Jeśli to ważne, być może po prostu używaj pętli bash, ponieważ jest to niewygodne w awk. Perl sprawia, że ​​jest to łatwe, ponieważ splitmoże przyjmować argument max-field arg, ale jego uruchomienie jest znacznie wolniejsze niż awk.

W rzeczywistości okazało się, że nie jest to takie trudne, tylko brzydkie wyrażenie regularne do napisania. Aby uzyskać resztę linii zamiast $5w awk, zapętlanie pól wciąż traci pierwotne białe znaki. Moim pierwszym praktycznym pomysłem jest użycie gensubna $0(całej linii), aby usunąć pierwsze 4 pola (tj. Spacja, po której następuje spacja), pozostawiając wszystko inne:

awk -vOFS=';' '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1); print $1, $2, $4, tail, $3 }' -- otherfile >file

Zrobiłem to poprawnie przy pierwszej próbie, ale fakt, że byłem pod wrażeniem samego siebie, mówi coś o czytelności tego kodu awk. >. <

Zwróć uwagę, jak to jest tak samo printjak poprzednio, ale z tailzamiast $5.

echo 'A  B c DD    e      f g    f' | 
  awk -vOFS=\; '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1);
   print $1, $2, $4, tail, $3 }'

A;B;DD;e       f g    f;c

Byłoby to bardziej imponujące, gdybym mógł skopiować / wkleić literał i pokazać, że przyszedł w wyniku. Wpisz jeden w bash za pomocą ^ Q. ctrl-Q oznacza Cytuj kolejne naciśnięcie klawisza jako dosłowny znak, ponieważ edycja linii w stylu emacsa basha jest w tym przypadku taka sama, jak faktyczna emacs.

http://mywiki.wooledge.org/BashFAQ zawiera przydatne informacje na temat skryptów w sposób, który nie ulegnie uszkodzeniu bez względu na to, jakie dane lub nazwy plików rzucasz na skrypt.

Peter Cordes
źródło