Як знайти файли в підкаталогах і сортувати їх за іменем файлу в одній команді?


9

Результат звичайної знахідки за допомогою find . ! -path "./build*" -name "*.txt":

./tool/001-sub.txt
./tool/000-main.txt
./zo/001-int.txt
./zo/id/002-and.txt
./as/002-mod.txt

і при сортуванні sort -n:

./as/002-mod.txt
./tool/000-main.txt
./tool/001-sub.txt
./zo/001-int.txt
./zo/id/002-and.txt

однак бажаний вихід:

./tool/000-main.txt
./zo/001-int.txt
./tool/001-sub.txt
./zo/id/002-and.txt
./as/002-mod.txt

що означає, що вихід сортується лише на основі імені файлу , але інформація про папки повинна зберігатися як частина виводу.

Редагувати : зробіть приклад складнішим, оскільки структура підкаталогу може включати більше одного рівня.


2
Дивіться це питання , я запитав на SO: stackoverflow.com/questions/3222810 / ...
CAMH

@camh - якщо можливо, я хотів би використовувати лише команди Unix. У будь-якому випадку, моє питання - це майже ваш дублікат. Чи можете ви передати найкраще рішення для цього потоку (будь-коли зберігати посилання на оригінал), щоб я міг позначити це як рішення?
unode

Якщо @Shawn вносить зміни, які я запропонував у своєму коментарі (використовувати -printfзамість awk), я вважаю, що це найкраще рішення. Я переробив свою оригінальну реалізацію для використання цього методу.
camh

Відповіді:


9

Вам потрібно сортувати за останнім полем (розглядаючи /як роздільник поля). На жаль, я не можу придумати інструмент, який може це зробити, коли кількість полів змінюється (якщо тільки sort -kможна прийняти негативні значення).

Щоб обійти це, вам доведеться зробити прикрасу-сортування-неприкосненість. Тобто візьміть ім’я файлу і поставте його на початку, а потім розділювач поля, потім зробіть сортування, потім видаліть перший стовпець і роздільник поля.

find . ! -path "./build*" -name "*.txt" |\
    awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
    sort -n -t / |\
    cut -f2- -d/

Ця awkкоманда говорить, що роздільник поля FS встановлений на /; це впливає на те, як воно читає поля. Для розділювача вивідного поля OFS також встановлено значення /; це впливає на спосіб друку записів. Наступне твердження говорить про друк останнього стовпця ( NFце кількість полів у записі, тому також трапляється індекс останнього поля), а також весь запис ( $0це весь запис); він надрукує їх разом з OFS. Потім список sortредагується, і він розглядається /як роздільник поля - оскільки ми маємо перше ім’я файлу в записі, він буде сортувати за цим. Потім cutдрукується лише поля 2 до кінця, знову вважаючи /роздільником поля.


3
Оскільки це з find (1), ви можете пропустити частину awk та використовувати-printf '%f/%p\n'
camh

насправді наша установка трохи складніше. Він включає в себе змінну глибину subdir. Відредагував питання, щоб відобразити цей факт. Вибачте за те, що спочатку не включив це.
unode

1
@Unode: Рішення Шона справляється зі змінною глибиною просто чудово, це канонічне рішення цієї проблеми (до незначних варіацій).
Жил "ТАК - перестань бути злим"

4

Я б використовував файли '-printf' для виводу імені та шляху, сортування за назвою та обрізання імені на останньому кроці. '###' - це лише маркер, який допомагає вирізати.

find -name "*.txt" -printf "%f###%p\n" | sort -n | sed 's/.*###//'

% f друкує ім'я файлу,% p весь шлях.

Я спростила команду find-команду, щоб перевести її в один рядок, звичайно, ви залишили б ! -path "./build*"частину.


3

У zsh ≥4.3.10:

print -l -- **/*.txt~build*(oe\''REPLY=${REPLY:t}'\')
  • **/*.txtвідповідає *.txtу поточному каталозі та його підкаталогах рекурсивно .
  • ~build* виключає збіги, текст яких починається з build*(як ! -path './build*'). (Вам потрібно setopt extended_globспочатку.)
  • (oe\''…'\')є класифікатором глобального сортування . REPLY=…будує рядок для сортування від рядка до повернення.
  • ${REPLY:t}є базовою назвою ("хвіст") шляху.

Багато зв'язаної магії. Цікаво, але ми обмежені синтаксисом sh. +1
скасувати
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.