La salida ? uno le mostrará ./ delante del nombre del archivo
Kiwy
Respuestas:
69
$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 a
12 b
0 foo
0 total
Como puede ver, el -carchivo se tomó como una opción duy no se informa (y ve la totallínea debido a du -c). Además, el archivo llamado a\n12\tbnos hace pensar que hay archivos llamados ay b.
$ du -hs --*0 a12 b0-c0 foo
Eso es mejor. Al menos este tiempo -cno se toma como una opción.
$ du -hs ./*0./a12 b0./-c0./foo
Eso es aun mejor. El ./prefijo evita que -cse tome como una opción y la ausencia de ./antes ben la salida indica que no hay ningún barchivo 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 cmdno 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.txten el directorio actual (establece la variable awk aen b.txtlugar de decirle que procese el archivo).
awk -f file.awk ./*
No tiene el problema porque ./ano es un nombre de variable awk válido, por ./a=b.txtlo 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 catque 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 fooson incorrectos porque se supone que los nombres de archivo no contienen caracteres de nueva línea ( wc -lcuenta los caracteres de nueva línea, los que se muestran greppara 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
Otras digresiones
1 . Siento que tengo que ampliar eso aquí, siguiendo la discusión en los comentarios.
$ du -hs ./*0./a12 b0./-c0./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:
Los personajes de control, las secuencias de escape pueden afectar la forma en que se muestran las cosas. Por ejemplo, \rmueve el cursor al comienzo de la línea, \bmueve el cursor hacia atrás, \e[Chacia adelante (en la mayoría de las terminales) ...
muchos caracteres son invisibles en un terminal que comienza con el más obvio: el carácter de espacio.
Hay caracteres Unicode que se parecen a la barra diagonal en la mayoría de las fuentes
$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*0./x0./x0./x0.∕x0./x0./x
Muchos xpero yfalta.
Algunas herramientas como GNUls 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 duno 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 lsque nuestro conjunto de caracteres era ASCII.
$ du -hs ./*| LC_ALL=C sed -n l0\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 sedimplementaciones compatibles con POSIX , pero tenga en cuenta que algunas sedimplementaciones antiguas no son tan útiles.
$ du -hs ./*| cat -vte0^I./x$0^I./x $0^I./x$0^I.M-bM-^HM-^Ux$
(no estándar pero bastante común, también cat -Acon 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 -vtc00000000 \t ./ x \n 0 \t ./ x \n 0 \t .0000020/ x \n 0 \t .342210225 x \n 0 \t ./ y0000040 \r 0 \t .033[ C x \n 0 \t ./ y \b x0000060 \n0000061
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 ynunca 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 duinforma el uso del disco, no informa otros enlaces a un archivo ya listado (no todas las duimplementaciones se comportan así cuando los enlaces duros están listados en la línea de comando).
+1, agradable y completo (por lo que puedo decir ^^). Especialmente me encanta la ventaja "grep -c /". También vale la pena señalar: la ventaja de "./*" sobre "*" aparece en una de las (muchas) buenas respuestas de las Preguntas Frecuentes de Unix (probablemente en faqs.org. Iirc, está en la pregunta acerca de cómo iniciar archivos con una "-").
Olivier Dulac
... y no es una mala práctica tener archivos con nuevas líneas y pestañas en sus nombres? Sé que trato de limitar los nombres a [a-z0-9.+-].
Blacklight Shining
55
@BlacklightShining, es muy malo robar autos, pero es malo dejar tu auto desbloqueado (ignorar las nuevas líneas), especialmente cuando es un auto costoso (script que se ejecuta como un usuario privilegiado, en un servidor con datos confidenciales ...) o cuando estacione en un área áspera ( /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 ...)
Stéphane Chazelas
1
@BlacklightShining, las nuevas líneas son inusuales, pero los espacios incrustados son muy comunes hoy en día, particularmente para archivos creados a través de GUI.
alexis
2
@BlacklightShining, sí, aunque ese (como "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 rootsupuesto). 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.
Stéphane Chazelas
6
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 12288Jan2320:04.
drwxr-xr-x.4 root root 4096Jul82013..-rw-rw-r--.1 saml saml 972Oct620:26 abcdefg
Puedes convencerte de que estas 2 listas son esencialmente lo mismo si usas echopara ver a qué se expandiría el shell.
$ echo *
$ echo ./*
Estos 2 comandos enumerarán todos los archivos en su directorio actual.
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!
Entonces, ¿por qué usar ./*?
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.
Respuestas:
Como puede ver, el
-c
archivo se tomó como una opcióndu
y no se informa (y ve latotal
línea debido adu -c
). Además, el archivo llamadoa\n12\tb
nos hace pensar que hay archivos llamadosa
yb
.Eso es mejor. Al menos este tiempo
-c
no se toma como una opción.Eso es aun mejor. El
./
prefijo evita que-c
se tome como una opción y la ausencia de./
antesb
en la salida indica que no hay ningúnb
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:o:
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 paraecho
).Hay casos donde
./*
resuelve problemas que--
no lo hacen. Por ejemplo:falla si hay un archivo llamado
a=b.txt
en el directorio actual (establece la variable awka
enb.txt
lugar de decirle que procese el archivo).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.falla si hay un archivo llamado
-
en el directorio actual, ya que le dicecat
que lea desde su stdin (-
es especial para la mayoría de las utilidades de procesamiento de texto y paracd
/pushd
).está bien porque
./-
no es especial paracat
.Cosas como:
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 muestrangrep
para cada archivo y los que están en los nombres de archivo). Deberías usar en su lugar:(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:./*
Sin embargo, puede tener algunos efectos secundarios no deseados.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:Daría salida:
en lugar de:
Otras digresiones
1 . Siento que tengo que ampliar eso aquí, siguiendo la discusión en los comentarios.
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 dedu -- *
) 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
(vea cómo va en su navegador).
Un ejemplo:
Muchos
x
peroy
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. GNUdu
no lo hace.Hay formas de hacer que se revelen:
Vea cómo se
∕
volvió???
después de que dijimosls
que nuestro conjunto de caracteres era ASCII.$
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 GNUsed
, debería ser lo mismo en todas lassed
implementaciones compatibles con POSIX , pero tenga en cuenta que algunassed
implementaciones antiguas no son tan útiles.(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).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 condu -hs *
eso que no tiene nada que ver con los nombres de archivo, pero debe tenerse en cuenta: debido a quedu
informa el uso del disco, no informa otros enlaces a un archivo ya listado (no todas lasdu
implementaciones se comportan así cuando los enlaces duros están listados en la línea de comando).fuente
[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 dedu ./*
. Probablemente debería agregar una nota al respecto. Lo hare mañana. Tenga en cuenta que antes quise decir privilegiado en el sentido general, noroot
(aunque se aplica aún más, porroot
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.Puedes convencerte de que estas 2 listas son esencialmente lo mismo si usas
echo
para ver a qué se expandiría el shell.Estos 2 comandos enumerarán todos los archivos en su directorio actual.
Ejemplos
Podemos hacer algunos datos falsos así:
Ahora, cuando usamos los
echo
comandos anteriores , vemos el siguiente resultado: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!
Entonces, ¿por qué usar ./*?
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.fuente