Constantemente veo respuestas que citan este enlace que dice definitivamente "¡No analizar ls
!" Esto me molesta por un par de razones:
Parece que la información en ese enlace ha sido aceptada al por mayor con pocas preguntas, aunque puedo detectar al menos algunos errores en la lectura informal.
También parece que los problemas establecidos en ese enlace no han provocado el deseo de encontrar una solución.
Del primer párrafo:
... cuando solicita
[ls]
una lista de archivos, hay un gran problema: Unix permite casi cualquier carácter en un nombre de archivo, incluidos espacios en blanco, líneas nuevas, comas, símbolos de barra y prácticamente cualquier otra cosa que alguna vez intente usar como delimitador excepto NUL. ...ls
separa los nombres de archivo con nuevas líneas. Esto está bien hasta que tenga un archivo con una nueva línea en su nombre. Y dado que no conozco ninguna implementaciónls
que le permita terminar los nombres de archivo con caracteres NUL en lugar de líneas nuevas, esto no nos permite obtener una lista de nombres de archivo de forma segurals
.
Bummer, ¿verdad? Como siempre podemos manejar una línea nueva terminada conjunto de datos que figuran para los datos que puedan contener saltos de línea? Bueno, si las personas que responden preguntas en este sitio web no hacen este tipo de cosas a diario, podría pensar que estamos en problemas.
Sin embargo, la verdad es que la mayoría de las ls
implementaciones en realidad proporcionan una API muy simple para analizar su salida y todos lo hemos estado haciendo todo el tiempo sin siquiera darnos cuenta. No solo puede finalizar un nombre de archivo con nulo, también puede comenzar uno con nulo o con cualquier otra cadena arbitraria que desee. Además, puede asignar estas cadenas arbitrarias por tipo de archivo . Por favor considera:
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
Mira esto para más.
Ahora es la siguiente parte de este artículo lo que realmente me atrapa:
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
El problema es que, a partir de la salida de
ls
, ni usted ni la computadora pueden saber qué partes constituyen un nombre de archivo. ¿Es cada palabra? No. ¿Es cada línea? No. No hay una respuesta correcta a esta pregunta que no sea: no se puede saber.Observe también cómo a
ls
veces confunde los datos de su nombre de archivo (en nuestro caso, convirtió el\n
carácter entre las palabras "a" y "nueva línea" en un signo de interrogación ......
Si solo desea iterar sobre todos los archivos en el directorio actual, use un
for
bucle y un globo:
for f in *; do
[[ -e $f ]] || continue
...
done
¡El autor lo llama nombres de archivo confusos cuando ls
devuelve una lista de nombres de archivo que contienen globos de shell y luego recomienda usar un globo de shell para recuperar una lista de archivos!
Considera lo siguiente:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX define los operandos -1
y -q
ls
así:
-q
- Forzar que cada instancia de caracteres de nombre de archivo no imprimibles ys<tab>
se escriban como el signo de interrogación ('?'
). Las implementaciones pueden proporcionar esta opción por defecto si la salida es a un dispositivo terminal.
-1
- (El dígito numérico uno.) Fuerza la salida a ser una entrada por línea.
Globbing no está exento de problemas: ?
coincide con cualquier carácter, por lo que múltiples ?
resultados coincidentes en una lista coincidirán con el mismo archivo varias veces. Eso es fácil de manejar.
Aunque la forma de hacer esto no es el punto, después de todo, no se necesita mucho para hacer y se demuestra a continuación, estaba interesado en por qué no . Según lo considero, la mejor respuesta a esa pregunta ha sido aceptada. Te sugiero que trates de concentrarte más a menudo en decirle a la gente lo que pueden hacer que en lo que no pueden. Es mucho menos probable, como creo, que se demuestre que está equivocado al menos.
Pero, ¿por qué intentarlo? Es cierto que mi motivación principal era que otros me decían que no podía. Sé muy bien que la ls
salida es tan regular y predecible como podrías desear, siempre y cuando sepas qué buscar. La información errónea me molesta más que la mayoría de las cosas.
Sin embargo, la verdad es que, con la notable excepción de las respuestas de Patrick y Wumpus Q. Wumbley (a pesar del increíble manejo de este último) , considero que la mayoría de la información en las respuestas aquí es en su mayoría correcta: un globo de concha es más fácil de usar y generalmente más efectivo cuando se trata de buscar en el directorio actual que el análisis ls
. Sin embargo, no son, al menos en mi opinión, razones suficientes para justificar la propagación de la información errónea citada en el artículo anterior ni son una justificación aceptable para " nunca analizarls
" .
Tenga en cuenta que los resultados inconsistentes de la respuesta de Patrick son principalmente el resultado de su uso en zsh
ese momento bash
. zsh
- por defecto - el $(
comando de división de palabras no sustituye los )
resultados de manera portátil. Entonces, cuando pregunta a dónde se fue el resto de los archivos. la respuesta a esa pregunta es que tu caparazón se los comió. Esta es la razón por la que necesita establecer la SH_WORD_SPLIT
variable al usar zsh
y tratar con código shell portátil. Considero que no haber notado esto en su respuesta es terriblemente engañoso.
La respuesta de Wumpus no calcula para mí: en un contexto de lista, el ?
personaje es un globo de shell. No sé cómo decir eso.
Para manejar un caso de resultados múltiples, debe restringir la codicia del globo. Lo siguiente solo creará una base de prueba de nombres de archivos horribles y lo mostrará por usted:
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
SALIDA
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
Ahora voy a salvo cada carácter que no es un /slash
, -dash
, :colon
, o carácter alfanumérico en un pegote cáscara continuación sort -u
la lista de resultados únicos. Esto es seguro porque ls
ya nos ha guardado los caracteres no imprimibles. Reloj:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
SALIDA:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
A continuación, vuelvo a abordar el problema pero utilizo una metodología diferente. Recuerde que, además de \0
nulo, el /
carácter ASCII es el único byte prohibido en un nombre de ruta. Puse los globos a un lado aquí y en su lugar combiné la -d
opción especificada POSIX para ls
y la -exec $cmd {} +
construcción también POSIX especificada para find
. Debido a find
que solo emitirá naturalmente uno /
en secuencia, lo siguiente proporciona fácilmente una lista de archivos recursiva y delimitada de manera confiable que incluye toda la información de rechazo para cada entrada. Solo imagine lo que podría hacer con algo como esto:
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
puede ser muy útil, especialmente cuando la unicidad del resultado está en duda.
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
Estos son solo los medios más portátiles que se me ocurren. Con GNU ls
podrías hacer:
ls --quoting-style=WORD
Y por último, aquí hay un método mucho más simple de análisisls
que uso con bastante frecuencia cuando necesito números de inodo:
ls -1iq | grep -o '^ *[0-9]*'
Eso solo devuelve números de inodo, que es otra práctica opción especificada por POSIX.
stat
en mi respuesta, ya que realmente comprueba que cada archivo existe. Su parte inferior con la sed
cosa no funciona.
ls
en primer lugar? Lo que estás describiendo es muy difícil. Necesitaré deconstruirlo para comprenderlo todo y soy un usuario relativamente competente. No puedes esperar que tu Joe promedio pueda lidiar con algo como esto.
ls
resultado del análisis es incorrecto se cubrieron bien en el enlace original (y en muchos otros lugares). Esta pregunta habría sido razonable si OP estuviera pidiendo ayuda para entenderla, pero en cambio OP simplemente está tratando de demostrar que su uso incorrecto está bien.
parsing ls is bad
. Hacer for something in $(command)
y confiar en la división de palabras para obtener resultados precisos es malo para la gran mayoría de los command's
cuales no tienen una salida simple.
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s vstime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28s