Respuestas:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Como puede ver, el -c
archivo se tomó como una opción du
y no se informa (y ve la total
línea debido a du -c
). Además, el archivo llamado a\n12\tb
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 este tiempo -c
no se toma como una opción.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Eso es aun mejor. El ./
prefijo evita que -c
se tome como una opción y la ausencia de ./
antes b
en la salida indica que no hay ningún b
archivo allí, pero hay un archivo con un carácter de nueva línea (pero vea más abajo 1 para más digresiones sobre eso).
Es una buena práctica usar el ./
prefijo cuando sea posible, y si no es así y para datos arbitrarios, siempre debe usar:
cmd -- "$var"
o:
cmd -- $patterns
Si cmd
no admite --
marcar el final de las opciones, debe informarlo como un error a su autor (excepto cuando sea por elección y documentado como para echo
).
Hay casos donde ./*
resuelve problemas que --
no lo hacen. Por ejemplo:
awk -f file.awk -- *
falla si hay un archivo llamado a=b.txt
en el directorio actual (establece la variable awk a
en b.txt
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 ./a=b.txt
lo que no se toma como una asignación de variable.
cat -- * | wc -l
falla si hay un archivo llamado -
en el directorio actual, ya que le dice cat
que lea 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
contar el número de archivos que contienen foo
son incorrectos porque se supone que los nombres de archivo no contienen caracteres de nueva línea ( wc -l
cuenta los caracteres de nueva línea, los que se muestran grep
para cada archivo y los que están en los nombres de archivo). Deberías usar en su lugar:
grep -l foo ./*.txt | grep -c /
(contar el número de /
caracteres es más confiable ya que solo puede haber uno por nombre de archivo).
Para recursivo grep
, el truco equivalente es usar:
grep -rl foo .//. | grep -c //
./*
Sin embargo, puede tener algunos efectos secundarios no deseados.
cat ./*
agrega dos caracteres más por archivo, por lo que lo haría alcanzar el límite del tamaño máximo de argumentos + entorno antes. Y a veces no desea que ./
se informe en la salida. Me gusta:
grep foo ./*
Daría salida:
./a.txt: foobar
en lugar de:
a.txt: foobar
1 . 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, ./
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 próximo ./
o el final de la salida).
Lo que eso significa es que la salida de du ./*
, al contrario de la de du -- *
) se puede analizar de manera confiable, 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:
\r
mueve el cursor al comienzo de la línea, \b
mueve el cursor hacia atrás, \e[C
hacia adelante (en la mayoría de las terminales) ...Hay caracteres Unicode que se parecen a la barra diagonal en la mayoría de las fuentes
$ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
/ ⁄ ∕ ╱ ⧸
(vea cómo va en su navegador).
Un ejemplo:
$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Muchos x
pero y
falta.
Algunas herramientas como GNU
ls reemplazarían los caracteres no imprimibles con un signo de interrogación (tenga en cuenta que ∕
(U + 2215) es imprimible sin embargo) 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
Vea cómo se ∕
volvió ???
después de que dijimos ls
que nuestro conjunto de caracteres era ASCII.
$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$
$
marca el final de la línea, para que podamos detectar el "x"
vs "x "
, todos los caracteres no imprimibles y no ASCII están representados por una secuencia de barra invertida (la barra invertida en sí misma se representaría con dos barras invertidas), lo que significa que no es ambigua. Eso era GNU sed
, debería ser lo mismo en todas las sed
implementaciones compatibles con POSIX , pero tenga en cuenta que algunas sed
implementaciones antiguas no son tan útiles.
$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$
(no estándar pero bastante común, también cat -A
con algunas implementaciones). Esa es útil y usa una representación diferente pero es ambigua ( "^I"
y <TAB>
se muestran de la misma manera, por ejemplo).
$ du -hs ./* | od -vtc
0000000 0 \t . / x \n 0 \t . / x \n 0 \t .
0000020 / x \n 0 \t . 342 210 225 x \n 0 \t . / y
0000040 \r 0 \t . 033 [ C x \n 0 \t . / y \b x
0000060 \n
0000061
Ese es estándar e inequívoco (y consistente de implementación en implementación) pero no tan fácil de leer.
Notarás que y
nunca apareció arriba. Ese es un problema completamente no relacionado con du -hs *
eso que no tiene nada que ver con los nombres de archivo, pero debe tenerse en cuenta: debido a que du
informa el uso del disco, no informa otros enlaces a un archivo ya listado (no todas las du
implementaciones se comportan así cuando los enlaces duros están listados en la línea de comando).
[a-z0-9.+-]
.
/tmp
) o en un área con muchos automóviles caros ( $HOME
) y es aún peor ir a un sitio de preguntas y respuestas y decir que siempre está bien no bloquear su automóvil sin especificar en qué condiciones (en un garaje cerrado, guión escribió solo por usted en una máquina no conectada a ninguna red o almacenamiento extraíble ...)
"b "
o "a\bb"
) engañaría a un usuario en un terminal, pero no un script que analiza la salida de du ./*
. Probablemente debería agregar una nota al respecto. Lo hare mañana. Tenga en cuenta que antes quise decir privilegiado en el sentido general, no root
(aunque se aplica aún más, por root
supuesto). se permiten nuevas líneas, ignorarlas es un error. los insectos tienen la costumbre de ser explotados. Tienes que medir el riesgo caso por caso. Las buenas prácticas de codificación pueden evitar los problemas en muchos casos. Ciertamente, en SE, debemos crear conciencia.
No hay diferencia entre una *
y ./*
en términos de lo que los archivos cualquiera de las listas voluntad. La única diferencia sería con la segunda forma, cada archivo tendría una barra diagonal con un ./
prefijo delante de ellos, lo que generalmente significa el directorio actual.
Recuerde que el .
directorio es una notación abreviada para el directorio actual.
$ ls -la | head -4
total 28864
drwx------. 104 saml saml 12288 Jan 23 20:04 .
drwxr-xr-x. 4 root root 4096 Jul 8 2013 ..
-rw-rw-r--. 1 saml saml 972 Oct 6 20:26 abcdefg
Puedes convencerte de que estas 2 listas son esencialmente lo mismo si usas echo
para ver a qué se expandiría el shell.
$ echo *
$ echo ./*
Estos 2 comandos enumerarán todos los archivos en su directorio actual.
Podemos hacer algunos datos falsos así:
$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5
Ahora, cuando usamos los echo
comandos anteriores , vemos el siguiente resultado:
$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5
Esta diferencia puede parecer innecesaria, pero hay situaciones en las que desea garantizar a las diversas herramientas de línea de comandos de Unix que les está pasando nombres de archivos a través de la línea de comandos, ¡y nada más!
Como señala la respuesta de @ Stephane , debido a la naturaleza de qué caracteres son legales al nombrar archivos y directorios en Unix, se pueden construir nombres de archivo peligrosos que tienen efectos secundarios inesperados cuando se pasan a varios comandos de Unix en la línea de comando.
Muy a menudo, el uso de ./
se utilizará para ayudar a garantizar que los nombres de archivo expandidos se consideren como nombres de archivo cuando se pasan como argumentos a los diversos comandos de Unix.