ls contenido de un directorio ignorando enlaces simbólicos


17

Tengo un directorio en el que me gustaría enumerar todo el contenido (archivos y subdirectorios) sin mostrar los enlaces simbólicos. Estoy usando utilidades GNU en Linux. La lsversión es 8.13.

Ejemplo:

Listado completo del directorio:

~/test$ ls -Gg
total 12
drwxrwxr-x 2 4096 Jul  9 10:29 dir1
drwxrwxr-x 2 4096 Jul  9 10:29 dir2
drwxrwxr-x 2 4096 Jul  9 10:29 dir3
-rw-rw-r-- 1    0 Jul  9 10:29 file1
-rw-rw-r-- 1    0 Jul  9 10:29 file2
lrwxrwxrwx 1    5 Jul  9 10:29 link1 -> link1
lrwxrwxrwx 1    5 Jul  9 10:30 link2 -> link2

Lo que me gustaría obtener

~/test$ ls -somthing (or bash hack)
total 12
dir1 dir2 dir3 file1 file2

NOTA: Mi motivación principal es hacer un grep recursivo (GNU grep 2.10) sin seguir los enlaces simbólicos.

Respuestas:


28

Para la pregunta indicada puede usar find:

find . -mindepth 1 ! -type l

enumerará todos los archivos y directorios en el directorio actual o cualquier subdirectorio que no sean enlaces simbólicos.

mindepth 1es solo omitir la .entrada del directorio actual. La carne de esto es la combinación de -type l, que significa "es un enlace simbólico", y !, que significa negar la siguiente prueba. En combinación, coinciden con cada archivo que no es un enlace simbólico. Esto enumera todos los archivos y directorios de forma recursiva, pero no enlaces simbólicos.

Si solo desea archivos regulares (y no directorios):

find . -type f

Para incluir solo los hijos directos de este directorio, y no todos los demás de forma recursiva:

find . -mindepth 1 -maxdepth 1

Puede combinar esas (y otras) pruebas juntas para obtener la lista de archivos que desea.

Para ejecutar un particular grepen cada archivo que coincida con las pruebas que está utilizando, use -exec:

find . -type f -exec grep -H 'some pattern' '{}' +

El '{}'será reemplazado con los archivos. El +es necesario decir findse realiza su comando. La opción -Hobliga a grep a mostrar un nombre de archivo incluso si se ejecuta con un solo archivo coincidente.


No muestra los archivos en los que se encuentra el patrón.
TheMeaningfulEngineer

Para eso corres grep(ver el último párrafo).
Michael Homer

No trabajo para mi Simplemente genera los patrones encontrados sin un nombre de archivo donde se encontró.
TheMeaningfulEngineer

2
Use grep -H, si lo tiene (ver man grep), o de lo contrario grep 'pat' '{}' /dev/null ';'para engañarlo haciéndole creer que hay varios archivos si no lo tiene.
Michael Homer

1
+no cumple ese requisito en el caso degenerado donde solo queda un archivo o un archivo al final.
Michael Homer

16

O, más simple:

ls -l | grep -v ^l

Explicación

ls -lsignifica listar en forma larga . Cuando hace esto, la primera cadena de caracteres proporciona información sobre cada archivo. El primer carácter indica de qué tipo es cada archivo. Si es un enlace simbólico, entonces les el primer carácter.

grep -v ^lsignifica filtrar ( -v) las líneas que comienzan con ( ^) an l.


Esto funciona muy bien, pero ¿cómo obtener solo nombres de carpetas? Yo quiero usar esto enfor i in
Luka

¿Puedes explicar este código por favor?
abg

Tenga en cuenta que esta solución no funciona si ommit -len ls. Entonces esto no funcionaría como se esperaba:ls | grep -v ^l
stamster

@Luka No se repite la salida de find, vea unix.stackexchange.com/questions/321697
Kusalananda

1
@abg: agregué una explicación. Espero eso ayude.
Geoff

7

Desde la versión 2.12 en adelante, la -ropción para GNU grep no desreferencia los enlaces simbólicos a menos que los especifique a mano:

-r, --recursivo

Lea todos los archivos de cada directorio, de forma recursiva, siguiendo los enlaces simbólicos solo si están en la línea de comando. Esto es equivalente a la opción -d recurse.

-R, --dereferencia-recursiva

Lea todos los archivos en cada directorio, de forma recursiva. Siga todos los enlaces simbólicos, a diferencia de -r.


¿Qué versión de grep es esa? Tengo -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.en 2.10
TheMeaningfulEngineer

grep (GNU grep) 2.14
tijagi

5

En zsh, esto sería fácil gracias a los calificadores glob :

grep -- PATTERN **/*(.)

El patrón **/atraviesa subdirectorios de forma recursiva. El calificador glob .restringe la coincidencia con los archivos normales.

Sin zsh, use find(vea la respuesta de Michael Horner ). Y en este caso particular, GNU grep puede hacer lo que quiera (es exactamente lo que grep -rhace), pero solo desde la versión 2.12, las versiones anteriores siguieron enlaces simbólicos.


2

Puedes usar $LS_COLORSpara hacer esto. Si su versión de lsadmite la especificación de los colores usando esa variable, puede definir la salida por tipo de archivo. Es un comportamiento incorporado y muy configurable. Así que creé algunos archivos para demostrar esto como:

for f in 9 8 7 6 5 4 3 2 1
    do touch "${f}file" && 
    ln -s ./"${f}file" ./"${f}filelink"
done

Entonces ahora haré:

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | cat

###OUTPUT###
1file


HERE_THERE_BE_A_LINK>>1filelink@
2file


HERE_THERE_BE_A_LINK>>2filelink@
3file

...
HERE_THERE_BE_A_LINK>>8filelink@
9file
...

Y los nulos también están ahí ...

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | sed -n l
1file$
$
$
\000HERE_THERE_BE_A_LINK>>\0001filelink@$
2file$
$
$
\000HERE_THERE_BE_A_LINK>>\0002filelink@$
3file$
...

Puede especificar todos o cualquier tipo de archivo. Sin embargo, hacerlo solo para un solo tipo de archivo podría no ser lo que desea, ya que lsha incorporado algunos valores de compilación predeterminados para escapes de terminal. Sería mucho mejor abordar la API como una interfaz única. Aquí hay un pequeño medio simple de analizar y asignar dircolorsvalores predeterminados configurados para el entorno actual :

LS_COLORS='rs=:no=//:lc=:rc=:ec=//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//:"
done) ls -l --color=always | cat

Su salida en mi directorio de inicio se ve así:

total 884
///-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//1/
//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//Desktop//
//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//Terminology.log/
//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//new
file/
//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//new
file
link/ -> /fi//./new
file/
//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//script.sh/*
//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//shot-2014-06-22_17-10-16.jpg/
//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//target.txt/

También puede ejecutar eso cat -Ay la única diferencia que encontrará es que verá $nuevas líneas: no se introducen caracteres no imprimibles ls --color=alwayscon esta configuración, solo lo que ve aquí.

ls inserta sus escapes de terminal predeterminados de esta manera:

${lc}${type_code}${rc}FILENAME${lc}${rs}${rc}

... donde los valores predeterminados para $lc (izquierda del código) , $rc (derecha del código) y $rs (restablecer) son:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...respectivamente. ${type_code}se utiliza para reemplazar los diversos fi (archivo normal - desarmado predeterminado) , di (directorio) , ln (enlace) y cualquier otro tipo de archivo que conozca. También hay $no (normal) que también está desarmado por defecto y que aquí está representado por //al principio de cada línea. Mi pequeño IFS=:bloque simple funciona simplemente insertando el nombre de cada configurable también como su propio valor y agregando una barra oblicua o dos, aunque los \0bytes NUL también lo harían.

Por defecto ls, también insertará uno $rsinmediatamente anterior a su primera salida $lc, pero esto no se representa con precisión aquí. En este caso, he especificado $ec (código final) que significa en $rstodos los casos; cuando se especifica, no obtiene un extra $rsentre $noy ${type_code}como lo haría de otra manera, solo se presenta inmediatamente después de un nombre de archivo y una vez al comienzo de la salida - Como puede ver en la barra extra en la cabecera de la línea uno.

Aquí hay un fragmento de mi propio $LS_COLORS

printf %s "$LS_COLORS"

rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:\
so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:\
or=40;31;01:su=37;41:sg=30;43:ca=30;41:\
tw=30;42:ow=34;42:st=37;44:ex=01;32:\
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:...

Y, en verdad, mi pequeño truco de shell es probablemente demasiado complicado: hay una interfaz ampliamente disponible para asignar estos valores. Pruebe dircolors -pen su cli y info dircolorspara obtener más información al respecto.

Puede envolver los nombres de archivo en cadenas arbitrarias. Puede comentarlos si lo desea. Puede especificar comportamientos similares basados ​​solo en la extensión del archivo. Realmente no hay mucho que no puedas especificar de esa manera.

Ahora tampoco estoy inventando todo esto: me enteré después de tropezar con el código fuente por accidente.

Con esta configuración particular lsemitirá:

  1. $no - una vez por registro al comienzo de cada registro

  2. ${type_code}- una vez que precede inmediatamente a cada nombre de archivo para incluir una abreviatura del tipo de archivo y siempre aparece en la misma línea y 7 campos delimitados por espacios en blanco después $no o inmediatamente después de ->denotar el objetivo de un enlace simbólico.

  3. $ec - una vez inmediatamente antes de la primera línea y luego solo una vez inmediatamente después de cada nombre de archivo.

  4. Todos los demás valores están vacíos.

Lo que sigue es un delimitado por nulo ls, y esta vez lo cat -Ausaré, sin embargo, sin él, se vería igual que el último ejemplo:

LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//\0:"
done) ls -l --color=always | cat -A

total 884$
^@//^@//-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//^@1^@//$
^@//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//^@Desktop^@///$
^@//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//^@Terminology.log^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//^@new$
file^@//$
^@//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//^@new$
file$
link^@// -> /fi//^@./new$
file^@//$
^@//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//^@script.sh^@//*$
^@//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//^@shot-2014-06-22_17-10-16.jpg^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//^@target.txt^@//$

Y para eliminar de manera confiable todos los enlaces simbólicos de una -llista única como esta, puede hacer un cambio simple:

LS_COLORS='rs=:no=//:lc=:rc=:ec=/ :'$(
set -- di fi mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=$fc/:"
done)ln=///: ls -l --color=always | sed ':ln
\|///|{N;\|\n//|!bln};s|.*//||'

Mis resultados después de correr se parecen a ...

total 884
-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 fi/1/ 
drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 di/Desktop/ /
-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 fi/Terminology.log/ 
-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 fi/new
file/ 
-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 ex/script.sh/ *
-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 fi/shot-2014-06-22_17-10-16.jpg/ 
-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 fi/target.txt/ 

Usando algún comando como el que hago arriba:

LSCOLORS=...$(...)fc1=///:fc2=///: ls ... | sed ...

... (donde fc1y fc2son los tipos de archivo enumerados después set --en la subshell) debería servir para eliminar de forma confiable cualquier combinación de tipos de archivo que desee de la lssalida, independientemente de los caracteres que puedan contener los nombres de archivo.


Je, bonito, +1 por originalidad. Sin embargo, parece ahogarse en los directorios, están impresos con un código de color ANSI confuso. De todos modos, ¿podría agregar una explicación de cómo funciona esto? Tal vez explicar lo que lc, nc, etc, son?
terdon

@terdon - Sólo están impresas con la norma ANSI ya se escapa encuentra en su entorno y en la compilación como valor predeterminado para ls's di=valor. Tendría que agregar di=:a la var para anular eso, y para todos los demás. Es lo que quiero decir con la API: usted aborda cada tipo de archivo, pero debe abordar la interfaz de la misma manera en todos los casos. Por lo tanto, establecer lnun camino e ignorar los divalores predeterminados no funcionará. También hay muchos valores predeterminados compilados ... Hmm. Su preguntando por lcy ncy esas cosas es válida - por completo - Ya lo hizo el otro día, sin embargo. Lo vincularé, creo.
mikeserv

Ouch, así que tendría que especificar todos los tipos de archivos posibles siempre o verificar lo que está presente de antemano. OK gracias.
terdon

@terdon - vea mi edición ahora mismo - lo hace todo en un par de líneas de shell, especificando cada tipo por su nombre de tipo y delimitando de manera confiable cada nombre de archivo. No hay no imprimibles introducidos tampoco. Y de todos modos hay una API para eso: es dircolors. Lee un archivo de configuración y genera un evalvalor var amigable con el shell . Es bastante fácil, pero también lo son las pocas líneas allí arriba. De todos modos, si quisieras usarlo dircolors, solo guarda dos archivos diferentes e invoca de esa manera. Además, le mostré el otro día cómo hacer ambas cosas a la vez con \0delimitadores NUL como parte de cada secuencia de escape.
mikeserv

@terdon - Traté de aclarar lo fácil que es establecer todos los valores que podrían compilarse - Creo que los obtuve todos, tal vez demasiados ... LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$( set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex; for fc do printf %s "$fc=/$fc//\0:"; done) ls -l --color=always | cat -A
mikeserv

0

La respuesta anterior lleva a lo siguiente:

 ls -1F | grep -i "/"

Curiosamente, dejar el 1 fuera del comando ls todavía da una lista.


1
¿Por qué grep -i? No es como si /fuera mayúscula o minúscula. lsel valor predeterminado es un formato de columna noe cuando la salida no es una terminal (aquí es una tubería).
Kusalananda

-2

Prueba este:

ls | grep -v " -> "

2
Eso fallará por varias razones, por ejemplo en un archivo llamado a -> b. También fallará en los nombres de archivo con nuevas líneas y otras extrañezas. Lea esto para saber por qué analizar ls es una mala idea.
terdon

@terdon Hay razones más fundamentales por las que esto fallará. O lstiene un alias ls -l, y esto devuelve columnas antes del nombre del archivo, o no lo es, y esto no hace nada especial sobre los enlaces simbólicos.
Gilles 'SO- deja de ser malvado'

@terdon - se lsanaliza a sí mismo. todos analizamos lscada vez que imprimimos en una terminal; hay una API . Publiqué una respuesta que demuestra algo así aquí, pero puedes hacer mucho más. Te lo mostré el otro día: tenías nombres de archivo delimitados por nulos.
mikeserv

@mikeserv Lo sé, lo sé. Sin embargo, explicar cómo hacerlo de forma segura requiere varios cientos de líneas, como usted mismo ha demostrado. Tal como está, esta respuesta adolece de todos los peligros de analizar ls y el OP claramente no es consciente de ellos.
terdon

@terdon, lsno se va a comer a tus hijos ni nada, es solo un programa. Si no explicas entonces se llevará varios cientos de explicarlo y varios cientos más para explicar por qué los varios cientos de líneas que acaba de vincular a están equivocados. Y además - esto es sólo dos líneas:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv

-3

Prueba este comando: ls -p | grep -v @


No, enumera los enlaces.
TheMeaningfulEngineer

@Alan: k prueba estofind -type f
Thushi

1
-1 porque: i) -pSimplemente agrega una barra diagonal a los directorios, está buscando -Fii) su findalternativa no incluirá una lista de directorios y iii) Pruebe sus respuestas antes de publicar.
terdon
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.