¿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,
r
mueve el cursor al principio de la línea,b
mueve el cursor hacia atrás,e[C
adelante (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.