¿Cuál es la diferencia entre du -sh * y du -sh ./* ?
Nota:Lo que me interesa es el * y ./* partes.
Respuesta aceptada:
$ touch ./-c $'an12tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Como puede ver, el -c El archivo se tomó como una opción para du y no se informa (y se ve el total línea debido a du -c ). Además, el archivo llamado an12tb nos hace pensar que hay archivos llamados a y b .
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Eso es mejor. Al menos esta vez -c no se toma como una opción.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Eso es aún mejor. El ./ prefijo previene -c de ser tomado como una opción y la ausencia de ./ antes de b en la salida indica que no hay b archivo allí, pero hay un archivo con un carácter de nueva línea (pero vea más abajo para más digresiones al respecto).
Es una buena práctica usar el ./ prefijo cuando sea posible, y si no y para datos arbitrarios, debe siempre usar:
cmd -- "$var"
o:
cmd -- $patterns
Si cmd no es compatible con -- para marcar el final de las opciones, debe informarlo como un error a su autor (excepto cuando es por elección y documentado como para echo ).
Hay casos en los que ./* resuelve problemas que -- no. Por ejemplo:
awk -f file.awk -- *
falla si hay un archivo llamado a=b.txt en el directorio actual (establece la variable awk a a b.txt en lugar de decirle que procese el archivo).
awk -f file.awk ./*
No tiene el problema porque ./a no es un nombre de variable awk válido, por lo que ./a=b.txt no se toma como asignación de variable.
cat -- * | wc -l
falla si hay un archivo llamado - en el directorio actual, ya que eso le dice a cat para leer desde su stdin (- es especial para la mayoría de las utilidades de procesamiento de texto y para cd /pushd ).
cat ./* | wc -l
está bien porque ./- no es especial para cat .
Cosas como:
grep -l -- foo *.txt | wc -l
para contar el número de archivos que contienen foo son incorrectos porque asume que los nombres de los archivos no contienen caracteres de nueva línea (wc -l cuenta los caracteres de nueva línea, los generados por grep para cada archivo y los de los propios nombres de archivo). Deberías usar en su lugar:
grep -l foo ./*.txt | grep -c /
(contando el número de / caracteres es más confiable ya que solo puede haber uno por nombre de archivo).
Para grep recursivo , el truco equivalente es usar:
grep -rl foo .//. | grep -c //
./* aunque puede tener algunos efectos secundarios no deseados.
cat ./*
agrega dos caracteres más por archivo, por lo que le permitiría alcanzar el límite del tamaño máximo de argumentos + entorno antes. Y a veces no quieres eso ./ que se informará en la salida. Me gusta:
grep foo ./*
Daría salida:
./a.txt: foobar
en lugar de:
a.txt: foobar
Digresiones adicionales
. Siento que tengo que ampliar eso aquí, siguiendo la discusión en los comentarios.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Arriba, ese ./ marcar el comienzo de cada archivo significa que podemos identificar claramente dónde comienza cada nombre de archivo (en ./ ) y dónde termina (en la nueva línea antes del siguiente ./ o el final de la salida).
Lo que eso significa es que la salida de du ./* , contrario al de du -- * ) se puede analizar de forma fiable, aunque no tan fácilmente en un script.
Sin embargo, cuando la salida va a una terminal, hay muchas más formas en que un nombre de archivo puede engañarlo:
-
Los caracteres de control, las secuencias de escape pueden afectar la forma en que se muestran las cosas. Por ejemplo,
rmueve el cursor al principio de la línea,bmueve el cursor hacia atrás,e[Cadelante (en la mayoría de las terminales)… -
muchos caracteres son invisibles en una terminal, empezando por el más obvio:el carácter de espacio.
-
Hay caracteres Unicode que tienen el mismo aspecto que la barra oblicua en la mayoría de las fuentes
$ printf 'u002f u2044 u2215 u2571 u29F8n' / ⁄ ∕ ╱ ⧸
(ver cómo va en su navegador).
Un ejemplo:
$ touch x 'x ' $'ybx' $'xn0t.u2215x' $'yr0t.e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Un montón de x pero y falta.
Algunas herramientas como GNU ls reemplazaría los caracteres no imprimibles con un signo de interrogación (tenga en cuenta que ∕ (U+2215) es imprimible) cuando la salida va a un terminal. GNU du no lo hace.
Hay formas de hacer que se revelen:
$ ls
x x x?0?.∕x y y?0?.?[Cx y?x
$ LC_ALL=C ls
x x?0?.???x x y y?x y?0?.?[Cx
Mira cómo ∕ convertido en ??? después de que le dijimos a ls que nuestro conjunto de caracteres era ASCII.
$ du -hs ./* | LC_ALL=C sed -n l0t./x$0t./x $0t./x$0t.342210225x$0t./yr0t.