¿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.

Respuestas:


274
for entry in "$search_dir"/*
do
  echo "$entry"
done

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.


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.


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

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

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

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.


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

-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

-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 */

¿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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.