Treść katalogu ignoruje dowiązania symboliczne

17

Mam katalog, w którym chciałbym wyświetlić całą zawartość (pliki i podkatalogi) bez pokazywania dowiązań symbolicznych. Korzystam z narzędzi GNU w systemie Linux. lsWersja to 8.13.

Przykład:

Pełna lista katalogów:

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

Co chciałbym dostać

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

UWAGA: Moją główną motywacją jest wykonanie rekurencyjnego grepa (GNU grep 2.10) bez następujących dowiązań symbolicznych.

TheMeaningfulEngineer
źródło

Odpowiedzi:

28

Do zadanego pytania możesz użyć find:

find . -mindepth 1 ! -type l

wyświetli listę wszystkich plików i katalogów w bieżącym katalogu lub dowolnych podkatalogach, które nie są dowiązaniami symbolicznymi.

mindepth 1jest po prostu pominąć pozycję .bieżącego katalogu. Mięso to połączenie -type l, co oznacza „jest dowiązaniem symbolicznym”, i !co oznacza zanegowanie następującego testu. W połączeniu pasują do każdego pliku, który nie jest dowiązaniem symbolicznym. Lista rekursywnie wyświetla wszystkie pliki i katalogi, ale nie zawiera dowiązań symbolicznych.

Jeśli chcesz tylko zwykłe pliki (a nie katalogi):

find . -type f

Aby uwzględnić tylko bezpośrednie elementy podrzędne tego katalogu, a nie wszystkie inne rekurencyjnie:

find . -mindepth 1 -maxdepth 1

Możesz połączyć te (i inne) testy razem, aby uzyskać listę żądanych plików.

Aby wykonać określoną operację grepna każdym pliku pasującym do używanych testów, użyj -exec:

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

'{}'Zostaną zastąpione plikami. Konieczne +jest, aby powiedzieć, findże polecenie zostało wykonane. Ta opcja -Hzmusza grep do wyświetlenia nazwy pliku, nawet jeśli akurat działa z jednym pasującym plikiem.

Michael Homer
źródło
Nie pokazuje plików, w których znaleziono wzór.
TheMeaningfulEngineer
Po to uciekasz grep(patrz ostatni akapit).
Michael Homer
Nie pracuje dla mnie Po prostu wyświetla znalezione wzorce bez nazwy pliku, w którym je znaleziono.
TheMeaningfulEngineer
2
Użyj grep -H, jeśli go masz (patrz man grep), lub w inny sposób, grep 'pat' '{}' /dev/null ';'aby oszukać, że istnieje wiele plików, jeśli nie masz.
Michael Homer
1
+nie spełnia tego wymagania w przypadku zdegenerowanym, w którym na końcu pozostaje tylko jeden plik lub jeden plik.
Michael Homer
17

Lub prościej:

ls -l | grep -v ^l

Wyjaśnienie

ls -loznacza listę w długiej formie . Gdy to zrobisz, pierwszy ciąg znaków podaje informacje o każdym pliku. Pierwszy znak wskazuje, jakiego typu jest każdy plik. Jeśli jest to dowiązanie symboliczne, lto pierwszy znak to an .

grep -v ^loznacza odfiltrowanie ( -v) linii rozpoczynających się od ( ^) an l.

Jonathan Petitcolas
źródło
Działa to świetnie, ale jak uzyskać tylko nazwy folderów? Chcę tego użyć wfor i in
Luka
Czy możesz wyjaśnić ten kod?
abg
Pamiętaj, że to rozwiązanie nie działa, jeśli ommit -lw ls. To nie zadziałałoby zgodnie z oczekiwaniami:ls | grep -v ^l
stamster
@Luka Nie zapętlasz danych wyjściowych find, patrz unix.stackexchange.com/questions/321697
Kusalananda
1
@abg - dodałem wyjaśnienie. Mam nadzieję, że to pomoże.
Geoff,
7

Począwszy od wersji 2.12, -ropcja dla GNU grep nie wyklucza dowiązań symbolicznych, chyba że podasz je ręcznie:

-r, --recursive

Odczytaj rekurencyjnie wszystkie pliki w każdym katalogu, podążając za dowiązaniami symbolicznymi, tylko jeśli znajdują się w wierszu poleceń. Jest to równoważne opcji -d recurse.

-R, - dereferencyjno-rekurencyjne

Odczytuj rekurencyjnie wszystkie pliki w każdym katalogu. Śledź wszystkie dowiązania symboliczne, w przeciwieństwie do -r.

tijagi
źródło
Jaka to wersja grep. Mam -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.w 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
tijagi,
5

W Zsh byłoby to łatwe dzięki globalnym kwalifikatorom :

grep -- PATTERN **/*(.)

Wzorzec **/rekurencyjnie przechodzi do podkatalogów. Kwalifikator glob .ogranicza dopasowanie do zwykłych plików.

Bez zsh użyj find(patrz odpowiedź Michaela Hornera ). W tym konkretnym przypadku GNU grep może robić, co chcesz (dokładnie to grep -rrobi) - ale tylko od wersji 2.12 wcześniejsze wersje korzystały z dowiązań symbolicznych.

Gilles „SO- przestań być zły”
źródło
2

Możesz użyć $LS_COLORSdo tego. Jeśli twoja wersja lsobsługuje określanie kolorów za pomocą tej zmiennej, możesz zdefiniować dane wyjściowe według typu pliku. Jest to wbudowane zachowanie i bardzo konfigurowalne. Więc stworzyłem kilka plików do pokazania:

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

Więc teraz zrobię:

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

A nulls też tam są ...

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

Możesz określić dla wszystkich lub dowolnych typów plików. Robi to tylko w przypadku pojedynczego typu pliku, ale nie jest to pożądane, ponieważ lswprowadzono pewne domyślne wartości kompilacji dla ucieczek terminali. O wiele lepiej byłoby zająć się interfejsem API jako pojedynczym interfejsem. Oto prosty mały sposób analizowania i przypisywania wartości dircolorsdomyślnych skonfigurowanych w bieżącym środowisku :

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

Jego wynik w moim katalogu domowym wygląda następująco:

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/

Możesz to również uruchomić, cat -Aa jedyną różnicą, jaką możesz napotkać, jest to, że zobaczysz $nowe wiersze - nie ma znaków, które nie można wydrukować, wprowadzonych przezls --color=always w tej konfiguracji - tylko to, co widzisz tutaj.

ls wstawia domyślny znak ucieczki terminala w ten sposób:

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

... gdzie wartości domyślne $lc (po lewej stronie kodu) , $rc (po prawej stronie kodu) i $rs (reset) to:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...odpowiednio. ${type_code}służy do zastępowania różnych fi (zwykły plik - domyślnie nieuzbrojony) , di (katalog) , ln (link) i każdego innego typu pliku, jaki znam. Istnieje również $no (normalny), który jest domyślnie nieuzbrojony i który jest tutaj reprezentowany przez //na początku każdej linii. Mój prosty mały IFS=:blok działa po prostu przez wstawienie nazwy dla każdego konfigurowalnego jako własnej wartości i dodanie ukośnika lub dwóch - chociaż \0NUL bajtów też by to zrobiło.

Domyślnie lswstawi również jedno $rsbezpośrednio poprzedzające jego pierwsze wyjście $lc- ale nie jest to tutaj dokładnie przedstawione. W tym przypadku podałem $ec (kod końcowy), który jest oznaczony$rs we wszystkich przypadkach - gdy jest określony, nie dostaniesz dodatkowego $rspomiędzy, $noa ${type_code}tak jak by to było inaczej - przedstawia tylko bezpośrednio po nazwie pliku i raz na początku wyjścia - jak widać w jednym dodatkowym ukośniku na początku pierwszej linii.

Oto fragment mojego własnego $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:...

I prawdę mówiąc, mój mały hack z powłoki jest prawdopodobnie zbyt skomplikowany - istnieje powszechnie dostępny interfejs do przypisywania tych wartości. Spróbuj dircolors -pw swoim cli i info dircolorsuzyskać więcej informacji na ten temat.

Możesz zawijać nazwy plików w dowolne ciągi. Możesz je skomentować, jeśli chcesz. Możesz określić podobne zachowania na podstawie tylko rozszerzenia pliku. Tak naprawdę nie ma tak wielu rzeczy, których nie można określić.

Teraz nie tylko wymyślam to wszystko - dowiedziałem się o tym po przypadkowym natknięciu się na kod źródłowy .

Dzięki tej konkretnej konfiguracji lswyemituje:

  1. $no - raz na rekord na początku każdego rekordu

  2. ${type_code}- jednokrotnie bezpośrednio poprzedzający każdą nazwę pliku, zawierającą skrót typu pliku i zawsze występujący w tym samym wierszu i 7 polach oddzielonych spacjami po $no lub bezpośrednio po ->oznaczeniu celu dowiązania symbolicznego.

  3. $ec - raz bezpośrednio przed pierwszym wierszem, a następnie tylko raz bezpośrednio po każdej nazwie pliku.

  4. Wszystkie pozostałe wartości są puste.

To, co następuje, jest rozdzielone znakiem null ls, i tym razem skorzystam cat -A, ale bez niego wyglądałoby to tak samo jak w poprzednim przykładzie:

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

Aby więc niezawodnie usunąć wszystkie dowiązania symboliczne z -llisty ong, takiej jak ta, możesz wprowadzić prostą zmianę:

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

Moje wyniki po uruchomieniu wyglądają jak ...

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/ 

Za pomocą polecenia podobnego do powyższego:

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

... (gdzie fc1i gdzie fc2typy plików są wymienione set --w dalszej części podpowłoki) powinny służyć do niezawodnego usuwania dowolnych kombinacji typów plików, które mogą być potrzebne, lsbez względu na znaki, które nazwy plików mogą zawierać.

mikeserv
źródło
Hej, miło, +1 za oryginalność. Wydaje się, że katalogi duszą się, są wydrukowane z nieczytelnym kodem kolorystycznym ANSI. W każdym razie, czy mógłbyś dodać wyjaśnienie, jak to działa? Być może wyjaśnić, co lc, ncitp są?
terdon
@terdon - Są drukowane tylko z ANSI ucieka już się w swoim środowisku i lub sporządzone jako domyślny dla ls„s di=wartości. Musisz dodać di=:do var, aby to anulować - i dla wszystkich innych. Mam na myśli interfejs API - adresujesz każdy typ pliku, ale musisz za każdym razem adresować ten sam interfejs. Więc ustawienie lnjednej drogi i zignorowanie diwartości domyślnych nie zadziała. Istnieje również wiele domyślnych ustawień ... Hmm. Twoje pytanie o lci nci rzeczy jest ważna - całkowicie - Już to zrobiłem to na drugi dzień chociaż. Myślę, że połączę to.
mikeserv
Ojej, więc musiałbym albo zawsze określać wszystkie możliwe typy plików, albo sprawdzać, co jest obecne? Ok dzięki.
terdon
@terdon - zobacz moją edycję właśnie teraz - robi to wszystko w kilku wierszach powłoki, określając każdy typ według nazwy typu i niezawodnie ograniczając każdą nazwę pliku. Nie wprowadzono również żadnych niedrukowalnych materiałów. I tak jest w tym interfejs API - to jest dircolors. Czyta plik konfiguracyjny i wyświetla evalprzyjazną dla powłoki wartość var. To całkiem proste - ale jest też kilka linii tam. W każdym razie, jeśli chcesz go użyć dircolors, po prostu zapisz dwa różne pliki i wywołaj go w ten sposób. Pokazałem ci też, jak postępować jednocześnie z \0ogranicznikami NUL w ramach każdej sekwencji ucieczki.
mikeserv
@terdon - Starałem się wyjaśnić, jak łatwo jest ustawić wszystkie wartości, które mogą zostać skompilowane - Myślę, że mam je wszystkie, może zbyt wiele ... 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

Powyższa odpowiedź prowadzi do:

 ls -1F | grep -i "/"

Co dziwne, pozostawienie 1 poza poleceniem ls wciąż daje listę.

woodsjay
źródło
1
Dlaczego grep -i? To nie tak, że /może być wielkie lub małe litery. lsdomyślnie jest to format kolumny noe, gdy wyjście nie jest terminalem (tutaj jest potok).
Kusalananda
-2

Spróbuj tego:

ls | grep -v " -> "
csny
źródło
2
To się nie powiedzie z różnych powodów, na przykład w pliku o nazwie a -> b. Nie powiedzie się również w nazwach plików z nowymi liniami i innych dziwnościach. Przeczytaj to, dlaczego parsowanie ls jest złym pomysłem.
terdon
@terdon Istnieją bardziej fundamentalne powody niepowodzenia. Albo lsjest aliasowane ls -l, a to zwraca kolumny przed nazwą pliku, albo nie jest, i to nie robi nic specjalnego w dowiązaniach symbolicznych.
Gilles „SO- przestań być zły”
@terdon - lsanalizuje się. wszyscy parsujemy za lskażdym razem, gdy drukujemy do terminala - jest interfejs API . Tutaj zamieściłem odpowiedź pokazującą coś takiego - ale możesz zrobić znacznie więcej. Pokazałem ci innego dnia - sam miałeś nazwy plików z ogranicznikami.
mikeserv
@ Mikeserv Wiem, wiem. Jednak wyjaśnienie, jak to zrobić bezpiecznie, zajmuje kilkaset linii, jak sam to wykazałeś. W obecnej formie odpowiedź ta wiąże się ze wszystkimi niebezpieczeństwami związanymi z analizą ls, a OP najwyraźniej ich nie zna.
terdon
@terdon - lsnie zje twoich dzieci ani nic takiego - to tylko program. Jeśli nie to wyjaśnić to będzie wziąć kilkaset to wyjaśnić i kilkaset więcej, aby wyjaśnić, dlaczego kilkaset wierszy po prostu związane są błędne. A poza tym - to tylko dwie linie:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Wypróbuj to polecenie: ls -p | grep -v @

Thushi
źródło
Nie, zawiera listę linków.
TheMeaningfulEngineer
@Alan: k spróbuj tegofind -type f
Thushi
1
-1, ponieważ: i) -ppo prostu dodaje ukośnik do katalogów, szukasz -Fii) Twoja findalternatywa nie będzie lista katalogów oraz iii) Proszę sprawdzić swoje odpowiedzi przed wysłaniem wiadomości.
terdon