ls содержимое каталога, игнорирующее символические ссылки

17

У меня есть каталог, в котором я хотел бы перечислить все содержимое (файлы и подкаталоги) без отображения символических ссылок. Я использую утилиты GNU в Linux. lsВерсия 8,13.

Пример:

Полный список каталогов:

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

Что бы я хотел получить

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

ПРИМЕЧАНИЕ. Моя основная мотивация - сделать рекурсивный grep (GNU grep 2.10) без следующих символических ссылок.

TheMeaningfulEngineer
источник

Ответы:

28

По заданному вопросу вы можете использовать find:

find . -mindepth 1 ! -type l

отобразит список всех файлов и каталогов в текущем каталоге или любых подкаталогах, которые не являются символическими ссылками.

mindepth 1просто пропустить .запись текущего каталога. Мясо этого является комбинацией -type l, что означает «является символической ссылкой», и !, что означает отрицание следующего теста. В сочетании они соответствуют каждому файлу, который не является символической ссылкой. Это список всех файлов и каталогов рекурсивно, но без символических ссылок.

Если вы просто хотите обычные файлы (а не каталоги):

find . -type f

Чтобы включить только прямые дочерние элементы этого каталога, а не все остальные рекурсивно:

find . -mindepth 1 -maxdepth 1

Вы можете объединить эти (и другие) тесты вместе, чтобы получить список файлов, которые вы хотите.

Для выполнения grepкаждого файла, соответствующего тестам, которые вы используете, используйте -exec:

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

'{}'Будут заменены файлами. +Надо сказать , findваша команда будет сделано. Опция -Hзаставляет grep отображать имя файла, даже если оно запускается с одним соответствующим файлом.

Майкл Гомер
источник
Он не показывает файлы, в которых найден шаблон.
TheMeaningfulEngineer
Это то, что вы бежите grep(см. Последний абзац).
Майкл Гомер
Я не работаю на меня. Он просто выводит найденные шаблоны без имени файла, где он был найден.
TheMeaningfulEngineer
2
Используйте grep -H, если у вас есть (см. man grep), Или иным образом, grep 'pat' '{}' /dev/null ';'чтобы обмануть его, думая, что есть несколько файлов, если у вас его нет.
Майкл Гомер
1
+не соответствует этому требованию в вырожденном случае, когда в конце остается только один файл или один файл.
Майкл Гомер
17

Или проще:

ls -l | grep -v ^l

объяснение

ls -lозначает перечислить в длинной форме . Когда вы делаете это, первая строка символов дает информацию о каждом файле. Первый символ указывает на тип каждого файла. Если это символическая ссылка, то lпервый символ.

grep -v ^lозначает отфильтровывать ( -v) строки, начинающиеся с ( ^) an l.

Джонатан Петитколас
источник
Это прекрасно работает, но как получить только имена папок? Я хочу использовать это вfor i in
Лука
Можете ли вы объяснить этот код, пожалуйста?
abg
Учтите , что это решение не работает , если вы ommit -lв ls. Так что это не будет работать, как ожидалось:ls | grep -v ^l
Stamster
@Luka Вы не зацикливаетесь на выводе find, см. Unix.stackexchange.com/questions/321697
Кусалананда
1
@abg - я добавил объяснение. Надеюсь, это поможет.
Джефф
7

Начиная с версии 2.12, -rопция для GNU grep не разыменовывает символические ссылки, если вы не укажете их вручную:

-r, --recursive

Читайте все файлы в каждом каталоге, рекурсивно, следуя символическим ссылкам, только если они находятся в командной строке. Это эквивалентно опции -d recurse.

-R, --dereference-рекурсивный

Читать все файлы в каждом каталоге, рекурсивно. Следуйте всем символическим ссылкам, в отличие от -r.

tijagi
источник
Что это за версия grep? У меня -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.в 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
Тиджаги
5

В zsh это было бы легко благодаря квалификаторам glob :

grep -- PATTERN **/*(.)

Шаблон **/рекурсивно пересекает подкаталоги. Спецификатор glob .ограничивает соответствие обычным файлам.

Без zsh используйте find(см . Ответ Майкла Хорнера ). И в этом конкретном случае GNU grep может делать то, что вы хотите (это именно то, что grep -rделает) - но только начиная с версии 2.12, более ранние версии действительно следовали по символическим ссылкам.

Жиль "ТАК - перестань быть злым"
источник
2

Вы можете использовать $LS_COLORSдля этого. Если ваша версия lsподдерживает указание цветов с помощью этой переменной, вы можете определить вывод для каждого типа файла. Это встроенное поведение и очень настраиваемый. Поэтому я создал несколько файлов для демонстрации, например:

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

Так что теперь я сделаю:

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

И нули тоже есть ...

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

Вы можете указать для всех или любых типов файлов. Делая это только для одного типа файла, вы, возможно, не захотите, поскольку в lsнего включены некоторые значения компиляции по умолчанию для экранирования терминала. Вы бы гораздо лучше обращались к API как к единому интерфейсу. Вот простой маленький способ синтаксического анализа и назначения текущих dircolorsнастроек среды по умолчанию:

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

Его вывод в мой домашний каталог выглядит так:

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/

Вы можете запустить это cat -Aтоже, и единственное отличие, с которым вы столкнетесь, это то, что вы увидите $новые строки - в ls --color=alwaysэтой конфигурации нет непечатаемых символов - только то, что вы видите здесь.

ls вставляет свой экранированный терминал по умолчанию следующим образом:

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

... где значения по умолчанию для $lc (слева от кода) , $rc (справа от кода) и $rs (сброса) :

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...соответственно. ${type_code}используется для обозначения различных fi (обычный файл - по умолчанию не установлено) , di (каталог) , ln (ссылка) и любого другого типа файла, который я знаю. Существует также $no (нормальный), который также по умолчанию не установлен и который представлен здесь //в начале каждой строки. Мой простой маленький IFS=:блок работает, просто вставляя имя для каждого конфигурируемого в качестве его собственного значения и добавляя косую черту или два - хотя \0байты NUL тоже подойдут.

По умолчанию lsтакже будет вставлен один, $rsнепосредственно предшествующий его первому выводу $lc- но это не точно представлено здесь. В этом случае я указал $ec (код конца), который заменяет во $rsвсех случаях - когда он указан, вы не получаете лишних $rsмежду $noи, ${type_code}как в противном случае - он представляет только сразу после имени файла и один раз в начале вывода - как вы можете видеть в одной дополнительной косой черте в начале первой строки.

Вот фрагмент из моего собственного $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:...

И, по правде говоря, мой маленький взлом оболочки, вероятно, слишком сложен - есть широко доступный интерфейс для назначения этих значений. Попробуйте dircolors -pв вашем кли и info dircolorsдля получения дополнительной информации об этом.

Вы можете заключить имена файлов в произвольные строки. Вы можете закомментировать их, если хотите. Вы можете указать аналогичное поведение только на основе расширения файла. Там действительно не так много, вы не можете указать таким образом.

Теперь я не просто все это придумываю - я узнал об этом после того , как случайно наткнулся на исходный код .

С этой конкретной конфигурацией lsбудет излучать:

  1. $no - один раз за запись в начале каждой записи

  2. ${type_code}- один раз непосредственно перед каждым именем файла, чтобы включить аббревиатуру типа файла и всегда находиться в той же строке, что и 7 полей с пробелами после $no или сразу после ->обозначения цели символической ссылки.

  3. $ec - один раз непосредственно перед самой первой строкой, а затем только один раз сразу после каждого имени файла.

  4. Все остальные значения пусты.

Далее следует нулевой разделитель ls, и на этот раз я буду использовать cat -A, хотя без него это выглядело бы так же, как в предыдущем примере:

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

И поэтому, чтобы надежно удалить все символические ссылки из -lсписка ong, как этот, вы можете сделать простое изменение:

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

Мои результаты после запуска выглядят так ...

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/ 

Используя некоторую команду, подобную той, что я делаю выше:

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

... (где fc1и fc2являются типами файлов, перечисленными после set --в подоболочке) должен служить для надежного удаления любых комбинаций типов файлов, которые вы можете захотеть lsвыводить независимо от любых символов, которые могут содержать имена файлов.

mikeserv
источник
Хех, приятно, +1 за оригинальность. Кажется, что каталоги задыхаются, они напечатаны с искаженным цветовым кодом ANSI. В любом случае, не могли бы вы объяснить, как это работает? Может быть, объяснить, что lc, ncи т. Д.?
Тердон
@terdon - Они печатаются только с ANSI ускользает уже установлены в вашей среде и или скомпилирован как по умолчанию для ls«S di=значения. Вам нужно будет добавить di=:в var, чтобы аннулировать это - и для всех остальных. Это то, что я имею в виду под API - вы обращаетесь к каждому типу файла, но вы должны обращаться к интерфейсу одинаково во всех случаях. Таким образом, установка lnодного способа и игнорирование diзначений по умолчанию не сработает. Есть также много стандартных значений по умолчанию ... Хм. Вы спрашиваете об этом, lcи ncвсе остальное верно - полностью - я уже сделал это на днях. Я свяжу это, я думаю.
mikeserv
Ой, так что мне придется либо всегда указывать все возможные типы файлов, либо проверять, что есть в наличии? Хорошо спасибо.
Тердон
@terdon - посмотрите мое редактирование только сейчас - он делает все это в нескольких строках оболочки, указывая каждый тип по имени типа и надежно разграничивая каждое имя файла. Также не введены непечатные. И в любом случае для этого есть API dircolors. Он читает конфигурационный файл и выдает evalдружественное для оболочки значение var. Это довольно легко - но так же, как и несколько строк там. В любом случае, если вы хотите использовать dircolorsего, просто сохраните два разных файла и запустите его таким образом. Кроме того, я показал вам на днях, как сделать оба сразу с \0разделителями NUL как часть каждой escape-последовательности.
mikeserv
@terdon - Я попытался прояснить, насколько легко установить все значения, которые могут быть скомпилированы - я думаю, что получил все, может быть, слишком много ... 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

Ответ выше приводит к следующему:

 ls -1F | grep -i "/"

Как ни странно, команда 1 из ls по-прежнему дает список.

woodsjay
источник
1
Почему grep -i? Это не так, как будто /может быть в верхнем или нижнем регистре. lsпо умолчанию используется формат noe-column, когда вывод не является терминалом (здесь это канал).
Кусалананда
-2

Попробуй это:

ls | grep -v " -> "
CSNY
источник
2
Это не удастся по разным причинам, например, для файла с именем a -> b. Он также не будет работать с именами файлов с символами новой строки и другой странностью. Пожалуйста, прочтите это, потому что разбор ls - плохая идея.
Тердон
@terdon Есть более фундаментальные причины, почему это не удастся. Либо lsимеет псевдоним ls -l, и это возвращает столбцы перед именем файла, либо это не так, и это не имеет ничего общего с символическими ссылками.
Жиль "ТАК - перестань быть злым"
@terdon - lsразбирает себя. мы все анализируем lsкаждый раз, когда печатаем в терминал - есть API . Я разместил ответ, демонстрирующий что-то подобное здесь - но вы можете сделать гораздо больше. Я показал вам на днях - у вас были имена файлов, разделенные нулем.
mikeserv
@mikeserv Я знаю, я знаю. Однако объяснение, как это сделать безопасно, занимает несколько сотен строк, как вы сами продемонстрировали. В таком виде этот ответ страдает от всех опасностей парсинга ls, и OP явно не знает о них.
Тердон
@terdon - lsне собирается есть своих детей или что-то еще - это всего лишь программа. Если вы не объяснить то , что будет принимать несколько сотен , чтобы объяснить и несколько сотен , чтобы объяснить , почему несколько сотен строк вы просто связаны с неверны. И кроме того - это только две строчки:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Попробуйте эту команду: ls -p | grep -v @

Thushi
источник
Нет, перечисляет ссылки.
TheMeaningfulEngineer
@Alan: k попробуйте этоfind -type f
Thushi
1
-1 потому что: i) -pпросто добавляет косую черту в каталоги, вы ищете -Fii) ваша findальтернатива не будет перечислять каталоги и iii) Пожалуйста, проверьте свои ответы перед публикацией.
Тердон