¿Qué significa este extraño símbolo ":>" en bash?

47

Encontré algo en un guión, pero no pertenecía al guión principal. Había :>en una línea.

¿Podrías explicarme lo que significa?

:> file
while read A B C D E; do echo "$A;$B;$D;$E;$C" >> file; done < otherfile
diego9403
fuente
66
Es importante destacar :>que no es un solo operador. Puede ser más fácil de entender si lo lees como en su : > filelugar.
jpfx1342
Esto significa que la persona que escribe el guión debería haber redirigido la salida del bucle en el archivo: while read A B C D E; do echo "$A;$B;$D;$E;$C"; done < otherfile > file. O mejor aún, deberían haber utilizado la herramienta adecuada para el trabajo, awk, como lo sugirió Peter . Como comentario aparte, casi siempre quieres usar el -rinterruptor conread .
Tom Fenech
Fuera de fiesta, sería un smiley para un cuervo.
smci

Respuestas:

46

Había:> en una línea de un script bash. Qué significa eso?

:> file

Es una forma abreviada de decir:

  • Si fileno existe, créelo; de lo contrario, truncarlo en 0bytes.

Esto significa que puede estar seguro de que fileexiste y está vacío.

También puedes usarlo > filepero :> filees más portátil.

Vea la pregunta de desbordamiento de pila ¿Cuál es el propósito de la ':' (colon) GNU Bash Builtin? para más información.

DavidPostill
fuente
No entiendo la segunda línea. Pensé que leer leer variables. El eco del comando también es extraño. ¿Podrías explicar?
diego9403
No soy un experto en Unix, pero creo que la segunda línea lee cosas otherfiley las echoenvía file. También crea variables a partir de lo que lea ... Si desea una respuesta definitiva, haga su propia pregunta.
DavidPostill
2
@ diego9403: readobtiene información de stdin. Por sí solo, leería lo que escribe. Dado que stdin se ha redirigido a, <otherfileentonces el contenido de otherfile"se escribe" en stdin. Entonces readobtiene los valores línea por línea en las variables $ A, $ B, $ C, $ D y $ E.
slebetman
Entonces, ¿es solo una alternativa más oscura truncatede coreutils?
Federico Poloni
1
@PeterCordes No quise decir "oscuro" como en "es poco común", sino como "es menos claro para el lector".
Federico Poloni
29

Parece una forma elegante de crear un nuevo archivo. En bash :es un comando nulo:

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

    No effect; the command does nothing.

    Exit Status:
    Always succeeds.

>redirige la salida de :a un archivo.

Arkadiusz Drabczyk
fuente
2
También truncará el archivo si ya existe ...
DavidPostill
2
sí, esto es lo que >hace
Arkadiusz Drabczyk
2
:es la abreviatura true. Posiblemente en algunas conchas, true¿no está incorporado? Ambos son incorporados en bash.
Peter Cordes
12

:es otro nombre para true. Ambas son conchas incorporadas en bash, pero no hay /bin/:, solo a /bin/true. La redirección de salida provoca el shell al open(2)archivo con O_CREAT|O_TRUNC. Si no se escribe nada, permanece en longitud cero.

Poner esas dos piezas juntas :> filees un modismo bastante común para truncar archivos. Sin embargo, la mayoría de la gente trataría de hacerlo menos extraño escribiendo : >file.


Como me preguntaste en un comentario sobre la segunda línea, convertiré mis comentarios en una respuesta. (aunque no hiciste esto en tu pregunta).

La segunda línea es un bucle que lee líneas de otherfilealgunas variables con nombre. El cuerpo del bucle se usa echopara imprimirlos con ;separadores en lugar de cualquier espacio en blanco que tenían antes. filese cierra y se vuelve a abrir (para agregar) cada iteración, porque la redirección está dentro del bucle. El uso while ...;do read -r ...;done <otherfile >filesería menos difícil y evitaría la necesidad de truncar primero el archivo. read -rno come \como un personaje de escape.

El procesamiento de texto en bash es bastante lento. Parte de eso es inevitable: readtiene que ir un byte a la vez (una read(2)llamada al sistema por byte) para evitar sobrepasar el final de una línea. Sería mejor usar la herramienta adecuada para el trabajo:

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

--significa que su script no se rompe si otherfilese le llama algo tonto --version.

Establecer el Separador de campo de salida en ;significa que puede pasar varios campos como argumentos para imprimir. Shell readasigna todo el resto de la línea con espacios en blanco a la última variable, pero no hay forma de decirle a awk que solo se divida en 5. Si eso es importante, tal vez solo siga usando un bucle bash, porque no es conveniente en awk. Perl lo hace fácil, ya que splitpuede tomar un argumento de campos máximos, pero es mucho más lento para iniciar que awk.

En realidad, resultó no ser tan difícil, solo una expresión regular fea para escribir. Para obtener el resto de la línea en lugar de $5en awk, recorrer los campos aún pierde su espacio en blanco original. Mi primera idea viable es usar gensuben $0(toda la línea) para eliminar los primeros 4 campos (es decir, sin espacio seguido de espacio), dejando todo lo demás:

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

Lo hice bien en el primer intento, pero el hecho de que estaba impresionado conmigo mismo por eso dice algo sobre la legibilidad de ese código awk. >. <

Tenga en cuenta que es igual printque antes, pero con tailen lugar de $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

Esto sería más impresionante si pudiera copiar / pegar el literal y mostrar que apareció en la salida. Escriba uno en bash con ^ Q. ctrl-Q significa Citar la siguiente pulsación de tecla como un carácter literal, ya que la edición de línea al estilo de emacs de bash es la misma que la de emacs real para esto.

http://mywiki.wooledge.org/BashFAQ tiene algunas cosas útiles acerca de las secuencias de comandos de manera que no se rompan sin importar qué datos o nombres de archivos arrojes a la secuencia de comandos.

Peter Cordes
fuente