Diferencia entre cat y '>' para poner a cero un archivo


23

¿Son estos dos comandos diferentes en la forma en que hacen cero los archivos? ¿Es esto último una forma más corta de hacer lo primero? ¿Qué está pasando detrás de escena?

Ambos

$ cat /dev/null > file.txt

$ > file.txt 

rendimiento

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt

Respuestas:


28

cat /dev/null > file.txtEs un uso inútil del gato .

Básicamente, cat /dev/nullsimplemente no genera catnada. Sí, funciona, pero muchos lo desaprueban porque provoca la invocación de un proceso externo que no es necesario.
Es una de esas cosas que es común simplemente porque es común.

Usar solo > file.txtfuncionará en la mayoría de los proyectiles, pero no es completamente portátil. Si quiere ser completamente portátil, las siguientes son buenas alternativas:

true > file.txt
: > file.txt

Tanto :y truede salida sin datos, y son órdenes internas de concha (mientras que cates una utilidad externa), por lo que son más ligeros y más 'adecuado'.

 

Actualizar:

Como tylerl mencionó en su comentario, también existe la >| file.txtsintaxis.

La mayoría de los shells tienen una configuración que les impedirá truncar un archivo existente mediante >. Debe usar >|en su lugar. Esto es para evitar errores humanos cuando realmente pretendía agregar >>. Puede activar el comportamiento con set -C.

Entonces, con esto, creo que el método más simple, más apropiado y portátil para truncar un archivo sería:

:>| file.txt

2
El comando de dos puntos se define en POSIX . Es una operación nula que existe para expandir los argumentos de la línea de comandos.
kojiro

3
LOL, "abuso de gato"
KM.

2
@kojiro :también tiene el mandato de POSIX para ser incorporado, y de hecho es diferente de trueque se considera un "especial" incorporado .
jw013

2
No te olvides de noclobber . >| filees un truncado más explícito.
tylerl

1
No truese requiere que no sea incorporado y tradicionalmente no lo fue. :Está construido en todos los depósitos de la familia Bourne. :es un archivo incorporado especial por POSIX (por lo que : > filesaldrá del shell, por ejemplo, si fileno se puede abrir para escribir en shells POSIX) y trueno lo es. POSIX incluso menciona que :puede ser más eficiente que trueen algunos sistemas.
Stéphane Chazelas

23

En términos de portabilidad:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Notas:

  1. excepto en sho kshemulación, para las redirecciones sin un comando, en zsh, se asume un comando predeterminado (un buscapersonas para la redirección de stdin solamente, de lo catcontrario), que se puede ajustar con las variables NULLCMD y READNULLCMD. Eso está inspirado en la característica similar en(t)csh
  2. Las redirecciones no se realizaron inicialmente :en UnixV7 como :se interpretó a mitad de camino entre un líder de comentarios y un comando nulo. Más tarde fueron y, como todos los componentes integrados, si la redirección falla, sale del caparazón.
  3. :y evalsiendo incorporados especiales, si falla la redirección, eso sale del shell ( bashsolo lo hace en modo POSIX).
  4. Curiosamente, en (t)csh, eso es definir una etiqueta nula (para goto), por lo que goto ''allí se ramificaría. Si la redirección falla, eso sale del shell.
  5. A menos que / si el comando correspondiente está disponible en $PATH( :por lo general no es, true, cat, cpy printfpor lo general son (POSIX les obliga)).
  6. Si la redirección falla, eso sale del shell.
  7. fileSin embargo, si es un enlace simbólico a un archivo no existente, algunas cpimplementaciones como GNU se negarán a crearlo.
  8. Sin embargo, las versiones iniciales del shell Bourne no admitían la redirección de los builtins.

En términos de legibilidad:

(esta sección es altamente subjetiva)

  • > file. Eso se >parece demasiado a un aviso o un comentario. Además, la pregunta que haré al leer eso (y la mayoría de los shells se quejarán de lo mismo) es ¿qué salida está redireccionando exactamente? .
  • : > file. :se conoce como el comando no-op. Eso se lee de inmediato como generar un archivo vacío. Sin embargo, aquí nuevamente, eso :puede perderse fácilmente y / o verse como un aviso.
  • true > file: ¿qué tiene que ver booleano con la redirección o el contenido del archivo? ¿Qué se quiere decir aquí? es lo primero que me viene a la mente cuando leo eso.
  • cat /dev/null > file. Concatenar /dev/nullen file? catsiendo a menudo visto como el comando para volcar el contenido del archivo, que todavía puede tener sentido: volcar el contenido de la archivo vacío enfile , un poco como una forma enrevesada de decir cp /dev/null filepero todavía comprensible.
  • cp /dev/null file. Copia el contenido del archivo vacío en file. Tiene sentido, aunque alguien que no sabe cómo cphacerlo de manera predeterminada podría pensar que también está tratando de hacer fileun nulldispositivo.
  • eval > fileo eval '' > file. No ejecuta nada y redirige su salida a a file. Tiene sentido para mi. Es extraño que no sea un idioma común.
  • printf '' > file: explícitamente no imprime nada en un archivo. El que tiene más sentido para mí.

En términos de rendimiento

La diferencia será si estamos usando un shell incorporado o no. Si no, un proceso tiene que ser bifurcado, el comando cargado y ejecutado.

evalEstá garantizado para ser construido en todos los depósitos. :está integrado donde esté disponible (a Bourne / csh le gusta). trueestá integrado solo en conchas tipo Bourne.

printfestá integrado en la mayoría de los modernos depósitos de tipo Bourne y fish.

cpy catgeneralmente no están integrados.

Ahora cp /dev/null fileno invoca redirecciones de shell, por lo que cosas como:

find . -exec cp /dev/null {} \;

serán más eficientes que:

find . -exec sh -c '> "$1"' sh {} \;

(aunque no necesariamente que:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

)

Personalmente

Personalmente, uso : > fileen conchas tipo Bourne, y no uso otra cosa que las conchas tipo Bourne en estos días.


¿Qué hay de dd of=file count=0?
kojiro

2
@kojiro, con algunas implementaciones de dd(como Solaris 10 al menos), count=0se ignora. dd if=/dev/null of=fileSería más portátil. En cualquier caso, eso es independiente del shell.
Stéphane Chazelas

OK, pero no es menos merecedor de inclusión que cp /dev/null file, ¿verdad?
kojiro

2
@kojiro, cp /dev/null filees un idioma común. Me limito a eso, el punto no es enumerar todas las formas posibles.
Stéphane Chazelas

5

Es posible que desee mirar truncate, que hace exactamente eso: truncar un archivo.

Por ejemplo:

truncate --size 0 file.txt

Esto es probablemente más lento que usarlo true > file.txt.

Sin embargo, mi punto principal es: truncateestá destinado a truncar archivos, mientras que usar> tiene el efecto secundario de truncar un archivo.


2
Truncar es bueno cuando quiere truncar un archivo a algo que no sea 0. Dicho esto, incluso sin un shell es una declaración extraña: ¿puede describir un contexto donde truncateestaría disponible, pero >ni las unistdbibliotecas de C estarían disponibles?
kojiro

Realmente no. Probablemente haya una solución más elegante para cada script o lenguaje de programación disponible.
Fabian

3
truncatees una utilidad FreeBSD, relativamente reciente (2008) agregada a los coreutils de GNU (aunque el --sizeestilo de opción larga de GNU es específico de GNU), por lo que no está disponible en sistemas que no sean GNU o FreeBSD, y no está disponible en sistemas GNU más antiguos, No diría que es portátil. cp /dev/null filefuncionaría sin una redirección de shell y sería más portátil.
Stéphane Chazelas

Bien, eliminaré ese comentario de portabilidad. Aunque su definición de reciente parece diferir.
Fabian

2

¡La respuesta depende un poco de lo que file.txtes y cómo el proceso le escribe!

Citaré un caso de uso común: tiene un archivo de registro en crecimiento llamado file.txty desea rotarlo.

Por lo tanto, copia, por ejemplo, file.txten file.txt.save, luego trunca file.txt.

En este escenario, SI el archivo no se abre another_process( por ejemplo, another_processpodría ser un programa que salga a ese archivo, por ejemplo, un programa que registra algo), entonces sus 2 propuestas son equivalentes y ambas funcionan bien (pero la segunda se prefiere como primero "cat / dev / null> file.txt" es un uso inútil de Cat y también se abre y lee / dev / null).

Pero el verdadero problema sería si other_processtodavía está activo y todavía tiene un identificador abierto que va al archivo.txt.

Luego, surgen 2 casos principales, dependiendo de cómo se other processabrió el archivo:

  • Si lo other_processabre de la manera normal, el controlador seguirá apuntando a la ubicación anterior en el archivo, por ejemplo, en el desplazamiento de 1200 bytes. Por lo tanto, la siguiente escritura comenzará en el desplazamiento 1200, y por lo tanto tendrá nuevamente un archivo de 1200bytes (+ lo que sea que se haya escrito en otro proceso), ¡con 1200 caracteres nulos iniciales! No es lo que quieres , supongo.

  • Si se other_processabre file.txten "modo anexar", cada vez que escribe, el puntero buscará activamente hasta el final del archivo. Por lo tanto, cuando lo trunca, "buscará" hasta el byte 0, ¡y no tendrá el efecto secundario negativo! Esto es lo que quieres (... ¡por lo general!)

Tenga en cuenta que esto significa que necesita, cuando trunca un archivo, asegurarse de que todos los que other_processtodavía escriben en esa ubicación lo hayan abierto en el modo "agregar". De lo contrario, deberá detenerlos other_processe iniciarlos nuevamente, para que comiencen a apuntar al comienzo del archivo en lugar de a la ubicación anterior.

Referencias: /programming//a/16720582/1841533 para obtener una explicación más clara y un pequeño ejemplo de la diferencia entre el registro en modo normal y anexado en /programming//a/984761/1841533


2
Muy poca de esta respuesta es relevante o responde a la pregunta. La diferencia entre a cat /dev/null > filey a > filees a cat /dev/nully eso no hace ninguna diferencia en el archivo.
jw013

@ jw013: ¡Cierto! Pero solo quería aprovechar la oportunidad de la pregunta para reafirmar la información de "lo que quieres / no lo que quieres", ya que no es muy conocida, y podría golpear fuertemente a alguien que intenta rotar los registros (un caso común en el que quieres truncar un archivo).
Olivier Dulac

1
Hay un tiempo y un lugar para todo. Su información puede ser útil en algún otro contexto, pero no pertenece aquí: debe encontrar un lugar más apropiado para ella porque nadie que intente rotar registros va a buscar en esta pregunta de redirección completamente no relacionada. Aquí su respuesta es el equivalente a una maleza digital, del mismo modo que una planta de calabaza útil en el medio de un campo de maíz se consideraría una maleza.
jw013

1

Me gusta y lo uso a menudo porque se ve más limpio y no como si alguien pulsara la tecla de retorno por accidente:

echo -n "" > file.txt

¿Debería ser un incorporado también?


3
Hay muchas formas de poner a cero un archivo. Creo que KM. solo estaba interesado en comprender la diferencia entre los dos métodos mostrados en la pregunta.
drs

66
Muchas echoimplementaciones no son compatibles -n(y se generarían -n<SPC><NL>aquí. printf '' > file.txtSerían más portátiles (al menos en los sistemas modernos / POSIX).
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.