¿Cómo obtener la lista de archivos en un directorio en un script de shell?

159

Estoy tratando de obtener el contenido de un directorio usando el script de shell.

Mi guión es:

for entry in `ls $search_dir`; do
    echo $entry
done

donde $search_dires un camino relativo. Sin embargo, $search_dircontiene muchos archivos con espacios en blanco en sus nombres. En ese caso, este script no se ejecuta como se esperaba.

Sé que podría usar for entry in *, pero eso solo funcionaría para mi directorio actual.

Sé que puedo cambiar a ese directorio, usar y for entry in *luego volver a cambiar, pero mi situación particular me impide hacerlo.

Tengo dos rutas relativas $search_diry $work_dirtengo que trabajar en ambas simultáneamente, leyéndolas creando / eliminando archivos, etc.

¿Entonces qué hago ahora?

PD: yo uso bash.

jrharshath
fuente

Respuestas:

274
for entry in "$search_dir"/*
do
  echo "$entry"
done
Ignacio Vazquez-Abrams
fuente
44
¿Puedes explicar por for entry in "$search_dir/*"qué no funciona? ¿Por qué necesitamos colocar /*fuera de las comillas?
mrgloom
66
@mrgloom: Porque debes dejar que el caparazón englobe el comodín.
Ignacio Vazquez-Abrams
No findsería más rápido?
Alexej Magura
44
La solución da el camino completo. ¿Qué sucede si solo quisiera enumerar lo que estaba en el directorio actual?
ThatsRightJack
1
@mrgloom si quieres hacerlo, puedes lograrlo confor entry in "${search_dir}/*"
funilrys
28

Las otras respuestas aquí son geniales y responden a su pregunta, pero este es el principal resultado de Google para "bash get list of files in directory", (que estaba buscando para guardar una lista de archivos), así que pensé en publicar un respuesta a ese problema:

ls $search_path > filename.txt

Si solo desea un determinado tipo (por ejemplo, cualquier archivo .txt):

ls $search_path | grep *.txt > filename.txt

Tenga en cuenta que $ search_path es opcional; ls> filename.txt hará el directorio actual.

tegan
fuente
No es necesario usar grep para obtener solo archivos .txt: `ls $ search_path / *. Txt> filename.txt '. Pero lo más importante, no se debe usar la salida del comando ls para analizar los nombres de archivo.
Victor Zamanian
1
@VictorZamanian, ¿puedes explicar por qué no deberíamos usar la salida de lspara analizar nombres de archivos? No he oído hablar de esto antes.
samurai_jane
1
@samurai_jane Hay muchos enlaces para proporcionar sobre este tema, pero aquí hay un primer resultado de búsqueda: mywiki.wooledge.org/ParsingLs . Incluso vi una pregunta aquí sobre SO alegando que las razones para no analizar el resultado de ls eran BS y fue muy elaborado al respecto. Pero las respuestas / respuestas aún afirmaban que era una mala idea. Echa un vistazo: unix.stackexchange.com/questions/128985/…
Victor Zamanian
26

Esta es una manera de hacerlo donde la sintaxis es más simple para mí entender:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./es el directorio de trabajo actual pero podría reemplazarse con cualquier ruta que
*.txtdevuelva cualquier cosa.
txt Puede verificar lo que se mostrará fácilmente escribiendo el lscomando directamente en el terminal.

Básicamente, crea una variable que yourfilenamescontiene todo lo que el comando de lista devuelve como un elemento separado, y luego lo recorre. El bucle crea una variable temporal eachfileque contiene un único elemento de la variable por la que está pasando, en este caso un nombre de archivo. Esto no es necesariamente mejor que las otras respuestas, pero lo encuentro intuitivo porque ya estoy familiarizado con el lscomando y la sintaxis del bucle for.

rrr
fuente
Esto funciona bien para un guión rápido e informal o una línea, pero se romperá si un nombre de archivo contiene nuevas líneas, a diferencia de las soluciones basadas en glob.
Soren Bjornstad
@SorenBjornstad gracias por el consejo! No sabía que se permitían nuevas líneas en los nombres de archivos, ¿qué tipo de archivos podrían tener? ¿Es esto algo que ocurre comúnmente?
rrr
Las nuevas líneas en los nombres de archivo son malas por este motivo y, hasta donde yo sé, no hay una razón legítima para usarlas. Nunca he visto uno en la naturaleza yo mismo. Dicho esto, es totalmente posible construir nombres de archivos con líneas nuevas de forma maliciosa para explotar esto. (Por ejemplo, imagine un directorio que contenga archivos A, By C. Cree archivos llamados B\nCy Dluego elija eliminarlos. El software que no maneja este derecho podría terminar eliminando los archivos preexistentes B y C, incluso si no tuviera permiso para hacer eso.)
Soren Bjornstad
19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done
ghostdog74
fuente
11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"
Noel Yap
fuente
1
Sé que esto es bastante viejo, pero parece que no puedo obtener el último xargs -0 -i echo "{}"comando, ¿quieres explicarme un poco? En particular, ¿qué hace la -i echo "{}"parte? También leí de la manpágina que -iahora está en desuso y deberíamos usar -Iinsted
drkg4b
1
-isustituye {}con el arg.
Noel Yap
¡Gracias! Esto es útil, también para los de mente lenta como yo, creo que {}es la cadena que se reemplaza con las coincidencias por el findcomando.
drkg4b
3
¿Por qué se utiliza xargs? de forma predeterminada, findimprime lo que encuentra ... puede eliminar todo de -print0.
gniourf_gniourf
1
Hacer eso no manejaría bien las entradas de archivos con espacios.
Noel Yap
4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Variaciones

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Notas:

  • . : carpeta actual
  • eliminar -maxdepth 1para buscar de forma recursiva
  • -type f: buscar archivos, no directorios ( d)
  • -not -path '*/\.*' : no volver .hidden_files
  • sed 's/^\.\///g': elimina el antepuesto ./de la lista de resultados
Victoria Stuart
fuente
0

Aquí hay otra forma de listar archivos dentro de un directorio (usando una herramienta diferente, no tan eficiente como algunas de las otras respuestas).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Emite todos los archivos del directorio actual. El forbucle itera sobre cada nombre de archivo e imprime en stdout.

Además, si busca directorios dentro del directorio, colóquelo dentro del forbucle:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d comprueba si el archivo es un directorio.

SnoopDogg
fuente
0

La respuesta aceptada no devolverá el prefijo de archivo con a. Para hacer eso use

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done
TrevTheDev
fuente
-1

En la versión de Linux con la que trabajo (x86_64 GNU / Linux) funciona a continuación:

for entry in "$search_dir"/*
do
  echo "$entry"
done
Andrushenko Alexander
fuente
-1: Esto es idéntico a la respuesta aceptada, excepto que no cita las variables. No citar $search_dirsignifica que se rompe si hay un espacio en el nombre del directorio. (En este caso, puede salirse con la suya sin citarlo $entry, pero sería una mejor práctica citarlo).
Soren Bjornstad
Está usted equivocado. Mi solución no es la misma, incluso cuando es similar a la respuesta aceptada. Y NO está mal, funciona. Esto es lo que probé en diferentes sistemas Unix, incluido Mac OS, y funcionó en cada uno de ellos. El archivo con espacios en blanco, incluso cuando tiene espacios en blanco en el nombre, se muestra correctamente. search_dir =. para la entrada en $ search_dir / *; hacer eco $ entrada; hecho ./ aa aaa.txt ./add ./apm_tables.txt Puede votar las soluciones solo cuando puede probar que la solución NO FUNCIONA o produce resultados incorrectos.
Andrushenko Alexander
Si lees mi comentario un poco más de cerca, verás que el problema está en los espacios en blanco en el nombre del directorio , no en el nombre del archivo . ¡Eso es diferente al problema con el que se encontró el interrogador, pero aún así significa que el código puede romperse involuntariamente! He aquí un ejemplo de lo que no funciona: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. El bucle se ejecuta dos veces, una vez imprimiendo xy la segunda vez y/*, lo que probablemente no sea el resultado esperado.
Soren Bjornstad
Y considero que las variables incorrectamente citadas son peligrosamente incorrectas, lo que es motivo de un voto negativo. Considere este código para eliminar cualquier subdirectorio del directorio de búsqueda, dejando los archivos: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Si también hay un xdirectorio en el directorio actual, se elimina sumariamente, ¡solo porque no citó su variable!
Soren Bjornstad
Bueno, siempre puede tomar un código creado para una tarea, cambiar la tarea ligeramente y el código se equivoca. Para la pregunta formulada, mi solución propuesta funciona. Pero ... como desarrollador, tengo que estar de acuerdo con sus argumentos: cuando podamos hacer un código más robusto y general, deberíamos hacerlo. Entonces agregaré las qoutes. Esto hará que mi respuesta sea idéntica a la respuesta dada anteriormente, pero en aras de la discusión (que puede ser útil para alguien) también dejo la respuesta.
Andrushenko Alexander
-1

Simplemente ingrese este comando simple:

ls -d */
Michael Sisko
fuente
¿Podría dar alguna explicación @Michael Sisko
Vipul
ls (lista) -d (lista solo directorios) * / (de cualquier patrón, la barra diagonal inversa restringe la lista solo a directorios).
Michael Sisko