È il contenuto di una directory che ignora i collegamenti simbolici


17

Ho una directory in cui vorrei elencare tutto il contenuto (file e sottodirectory) senza mostrare i collegamenti simbolici. Sto usando le utility GNU su Linux. La lsversione è 8.13.

Esempio:

Elenco completo delle directory:

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

Cosa vorrei ottenere

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

NOTA: la mia motivazione principale è fare un grep ricorsivo (GNU grep 2.10) senza seguire i collegamenti simbolici.

Risposte:


28

Per la domanda dichiarata puoi usare find:

find . -mindepth 1 ! -type l

elencherà tutti i file e le directory nella directory corrente o tutte le sottodirectory che non sono collegamenti simbolici.

mindepth 1è solo per saltare la .voce della directory corrente. La sua carne è la combinazione di -type l, che significa "è un collegamento simbolico" e !, che significa annullare il seguente test. In combinazione corrispondono a tutti i file che non sono un collegamento simbolico. Questo elenca tutti i file e le directory in modo ricorsivo, ma nessun link simbolico.

Se vuoi solo file regolari (e non directory):

find . -type f

Per includere solo i figli diretti di questa directory e non tutti gli altri in modo ricorsivo:

find . -mindepth 1 -maxdepth 1

È possibile combinare questi (e altri) test insieme per ottenere l'elenco dei file desiderati.

Per eseguire un particolare grepsu ogni file corrispondente ai test che stai utilizzando, usa -exec:

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

Il '{}'sarà sostituito con i file. Il +è necessario per raccontare findil vostro comando è fatto. L'opzione -Himpone a grep di visualizzare un nome file anche se accade con un singolo file corrispondente.


Non mostra i file in cui è stato trovato il modello.
TheMeaningfulEngineer,

Questo è ciò per cui corri grep(vedi l'ultimo paragrafo).
Michael Homer,

Non lavoro per me. Emette semplicemente i modelli trovati senza un nome file dove è stato trovato.
TheMeaningfulEngineer,

2
Usa grep -H, se ce l'hai (vedi man grep), o altrimenti grep 'pat' '{}' /dev/null ';'per indurlo a pensare che ci sono più file se non lo fai.
Michael Homer,

1
+non soddisfa tale requisito nel caso degenerato in cui è rimasto solo un file o un file alla fine.
Michael Homer,

16

Oppure, più semplice:

ls -l | grep -v ^l

Spiegazione

ls -lsignifica elencare in forma lunga . Quando lo fai, la prima stringa di caratteri fornisce informazioni su ciascun file. Il primo carattere indica il tipo di ciascun file. Se è un collegamento simbolico, allora lè il primo carattere.

grep -v ^lsignifica filtrare ( -v) le righe che iniziano con ( ^) an l.


Funziona benissimo, ma come ottenere solo i nomi delle cartelle? Voglio usarlo infor i in
Luka il

Puoi spiegare questo codice per favore?
abg

Attenzione che questa soluzione non funziona se si ommit -lin ls. Quindi questo non funzionerebbe come previsto:ls | grep -v ^l
stamster

@Luka Non esegui il ciclo sull'output di find, vedi unix.stackexchange.com/questions/321697
Kusalananda

1
@abg - Ho aggiunto una spiegazione. Spero che sia d'aiuto.
Geoff

7

Dalla versione 2.12 in poi, l' -ropzione per GNU grep non distingue i collegamenti simbolici a meno che non vengano specificati manualmente:

-r, - ricorsivo

Leggi tutti i file in ciascuna directory, ricorsivamente, seguendo i collegamenti simbolici solo se si trovano sulla riga di comando. Ciò equivale all'opzione -d recurse.

-R, --dereference-ricorsivo

Leggi tutti i file in ciascuna directory, in modo ricorsivo. Segui tutti i collegamenti simbolici, a differenza di -r.


Quale versione di grep è quella. Ho -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.in 2.10
TheMeaningfulEngineer il

grep (GNU grep) 2.14
tijagi

5

In zsh, questo sarebbe facile grazie ai qualificatori glob :

grep -- PATTERN **/*(.)

Il modello **/attraversa ricorsivamente le sottodirectory. Il qualificatore glob .limita la corrispondenza con i file normali.

Senza zsh, usa find(vedi la risposta di Michael Horner ). E in questo caso particolare, GNU grep può fare quello che vuoi (è esattamente quello che grep -rfa) - ma solo dalla versione 2.12, le versioni precedenti hanno seguito collegamenti simbolici.


2

Puoi usare $LS_COLORSper fare questo. Se la tua versione di lssupporta la specifica dei colori usando quella variabile, puoi definire l'output per tipo di file. È un comportamento integrato e molto configurabile. Quindi ho creato alcuni file per dimostrarlo come questo:

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

Quindi ora farò:

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

E ci sono anche i null ...

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

È possibile specificare per tutti o tutti i tipi di file. Farlo solo per un singolo tipo di file potrebbe non farti desiderare, dato che lsha incorporato alcuni valori di compilazione predefiniti per le escape di terminale. Faresti molto meglio ad affrontare l'API come un'unica interfaccia. Ecco un piccolo e semplice mezzo per analizzare e assegnare le dircolorsimpostazioni predefinite dell'ambiente corrente :

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

Il suo output nella mia directory home è simile al seguente:

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/

Puoi eseguirlo cat -Aanche con e l'unica differenza che incontrerai è che vedrai $per le nuove righe - non ci sono caratteri non stampabili introdotti da ls --color=alwaysquesta configurazione - solo ciò che vedi qui.

ls inserisce i suoi escape di terminale predefiniti in questo modo:

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

... dove i valori predefiniti per $lc (a sinistra del codice) , $rc (a destra del codice) e $rs (ripristina) sono:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...rispettivamente. ${type_code}viene utilizzato per sostituire i vari fi (file normale - impostazione predefinita non impostata) , di (directory) , ln (collegamento) e ogni altro tipo di file che conosco. C'è anche $no (normale) che è anche di default non impostato e che è qui rappresentato //all'inizio di ogni riga. Il mio semplice piccolo IFS=:blocco funziona semplicemente inserendo il nome di ogni configurabile anche come suo valore e aggiungendo una barra o due - sebbene anche i \0byte NUL lo farebbero.

Di default lsinserirà anche uno che $rsprecede immediatamente il suo primo output $lc, ma qui non è rappresentato con precisione. In questo caso ho specificato $ec (codice finale) che sostituisce in $rstutti i casi - quando viene specificato non si ottiene un extra $rstra $noe ${type_code}come si farebbe altrimenti - si presenta solo immediatamente dopo un nome file e una volta all'inizio dell'output - come puoi vedere nell'ultima barra in testa alla prima riga.

Ecco un frammento del mio $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:...

E, in verità, il mio piccolo hack shell è probabilmente eccessivamente complicato - c'è un'interfaccia ampiamente disponibile per assegnare questi valori. Prova dircolors -pnel tuo cli e info dircolorsper ulteriori informazioni al riguardo.

Puoi racchiudere i nomi dei file in stringhe arbitrarie. Puoi commentarli se lo desideri. È possibile specificare comportamenti simili basati solo sull'estensione del file. Non c'è davvero molto che non puoi specificare in questo modo.

Ora non sto solo inventando tutto questo - l'ho imparato dopo aver inciampato su codice sorgente .

Con questa particolare configurazione lsemetterà:

  1. $no - una volta per record all'inizio di ogni record

  2. ${type_code}- una volta immediatamente precedente a ciascun nome file per includere un'abbreviazione del tipo di file e che si verificano sempre sulla stessa riga di e 7 campi delimitati da spazi bianchi dopo $no o immediatamente dopo una ->denotazione del target di un collegamento simbolico.

  3. $ec - una volta che precede immediatamente la prima riga e successivamente solo una volta immediatamente dopo ogni nome di file.

  4. Tutti gli altri valori sono vuoti.

Quello che segue è delimitato da null ls, e questa volta userò cat -A, anche se, senza di esso, sembrerebbe lo stesso dell'ultimo esempio:

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^@//$

Quindi, per rimuovere in modo affidabile tutti i collegamenti simbolici da un -lelenco ong come questo, è possibile apportare una semplice modifica:

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

I miei risultati dopo aver eseguito sembrano come ...

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 un comando simile a quello che faccio sopra:

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

... (dove fc1e fc2sono i tipi di file elencati di seguito set --nella sottostruttura) dovrebbero servire a rimuovere in modo affidabile qualsiasi combinazione di tipi di file che si potrebbe desiderare lsdall'output indipendentemente da qualsiasi carattere che i nomi dei file potrebbero contenere.


Eh, carino, +1 per originalità. Sembra soffocare sulle directory, tuttavia sono stampate con un codice colore ANSI confuso. Ad ogni modo, potresti aggiungere una spiegazione di come funziona? Forse spiegare che cosa lc, ncecc sono?
Terdon

@terdon: vengono stampati solo con le escape ANSI già impostate nel proprio ambiente e compilate come predefinite per lsil di=valore di. Dovresti aggiungere di=:al var per annullare ciò - e per tutti gli altri. È ciò che intendo per api: indirizzi ogni tipo di file ma devi sempre indirizzare l'interfaccia allo stesso modo. Quindi impostare lnun modo e ignorare le diimpostazioni predefinite non funzionerà. Ci sono anche molte impostazioni predefinite compilate in ... Hmm. Il tuo Chiedendo lced nce roba è valida - completamente - ho già fatto l'altro giorno però. Lo collegherò, credo.
Mikeserv,

Ahi, quindi dovrei specificare sempre tutti i possibili tipi di file o controllare cosa è presente prima? Ok grazie.
terdon

@terdon - guarda la mia modifica proprio ora - fa tutto in un paio di righe di shell, specificando ogni tipo con il suo nome e delimitando in modo affidabile ogni nome di file. Non sono stati introdotti né stampabili. E c'è comunque un'API per quello - lo è dircolors. Legge un file di configurazione e genera un evalvalore var adatto alla shell . È abbastanza facile, ma lo sono anche le poche righe lassù. Ad ogni modo, se si desidera utilizzare, dircolorsè sufficiente salvare due file diversi e richiamarlo in quel modo. Inoltre, ti ho mostrato l'altro giorno come fare entrambi contemporaneamente con i \0delimitatori NUL come parte di ogni sequenza di escape.
Mikeserv,

@terdon - Ho cercato di chiarire quanto sia facile impostare tutti i valori che potrebbero essere compilati - Penso di 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
averli presi

0

La risposta sopra porta a quanto segue:

 ls -1F | grep -i "/"

Stranamente, lasciando il comando 1 fuori da ls viene comunque visualizzato un elenco.


1
Perché grep -i? Non è che /possa essere maiuscolo o minuscolo. lsil formato predefinito è noe-column quando l'output non è un terminale (qui è una pipe).
Kusalananda

-2

Prova questo:

ls | grep -v " -> "

2
Ciò fallirà per vari motivi, ad esempio su un file chiamato a -> b. Fallirà anche sui nomi dei file con newline e altre stranezze. Si prega di leggere questo per perché l'analisi di ls è una cattiva idea.
terdon

@terdon Ci sono ragioni più fondamentali per cui questo fallirà. O lsè aliasato ls -l, e questo restituisce colonne prima del nome del file, oppure non lo è, e questo non fa nulla di speciale sui collegamenti simbolici.
Gilles 'SO- smetti di essere malvagio' il

@terdon: lsanalizza se stesso. analizziamo tutti lsogni volta che stampiamo su un terminale - c'è un'API . Ho pubblicato una risposta dimostrando qualcosa di simile qui - ma puoi fare molto di più. Ti ho mostrato solo l'altro giorno: tu stesso avevi nomi di file delimitati da null.
Mikeserv,

@mikeserv Lo so, lo so. Tuttavia, spiegare come farlo in modo sicuro richiede diverse centinaia di righe, come tu stesso hai dimostrato. Allo stato attuale, questa risposta soffre di tutti i pericoli dell'analisi di LS e l'OP chiaramente non ne è consapevole.
terdon

@terdon - lsnon mangerà i tuoi figli o altro - è solo un programma. Se non lo spieghi, ci vorranno diverse centinaia per spiegarlo e altre centinaia per spiegare perché le diverse centinaia di righe a cui hai appena collegato sono sbagliate. E inoltre - sono solo due righe:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv,

-3

Prova questo comando: ls -p | grep -v @


No, elenca i collegamenti.
TheMeaningfulEngineer,

@Alan: k prova questofind -type f
Thushi,

1
-1 perché: i) Il -psolo aggiunge una barra alle directory, stai cercando -Fii) la tua findalternativa non elencherà le directory e iii) Ti preghiamo di testare le tue risposte prima di pubblicare.
terdon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.