знайти | xargs shasum створює контрольну суму файлу контрольної суми (передчасно) і виходить з ладу під час перевірки


10

Моя проблема (у сценарії з #!/bin/sh) полягає в наступному: я намагаюся перевірити всі файли в каталозі для архівних цілей. Файл контрольної суми (в моєму випадку sha1) з усіма іменами файлів повинен міститися в одному каталозі. Скажімо, у нас є каталог ~/testз файлами f1та f2:.

mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2

Тепер обчислюємо контрольні суми за допомогою

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum

робить саме те, що я хочу, він перераховує всі файли поточного каталогу і обчислює ша1 суми (maxdepth може бути змінено пізніше). Вихід на STDOUT:

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

На жаль, при спробі зберегти це у файл із

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1

отриманий файл відображає контрольну суму для себе:

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

і тому не вдається пізніше shasum --checkчерез очевидну проблему додаткової модифікації файлу при збереженні останньої суми.

Я озирнувся і, використовуючи -pпрапор для xargs, я виявив, що він якимось чином створює вихідний файл перед тим, як навіть виконати команду find, тому додатковий файл знайдеться і буде перевірено ...

Я знаю, що як вирішення проблеми я міг би зберегти контрольну суму в інше місце (тимчасовий каталог через mktemp) або виключити її в пошуку спеціально, але я хотів би зрозуміти, чому вона поводиться так, як це відбувається - що, на мої очі, не так корисно, наприклад, якщо перша команда перевірить, чи файл виводу вже на диску, він ніколи не отримає правильну відповідь ...


8
Це не той xargs, що саме оболонка створює цей файл, тому що перед виконанням будь-якої команди спочатку оболонка перенаправляє всі вхідні, вихідні та трубові, так що при findзапуску вихідний файл вже існує. Використовуйте -execзамість цього:find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
jimmij

@jimmij, це не гарантовано працюватиме, якщо необхідно кілька shвикликів. Зауважте, що вам потрібен аргумент для $0цього {}.
Stéphane Chazelas

@jimmij Ваша інша відповідь, яка запропонувала tee, зникла? Я спробував це, і він працює добре, я також придушив STDOUT з додаванням 1>/dev/null. Чи щось не було у відповіді або це помилка?
користувач121391

@ user121391 Стефан зазначив, що іноді може виникнути проблема з умовами гонки, що здається правдою. Я певний час відмінив його, щоб ви могли подивитися, але якщо у вас є багато файлів у списку, ця команда може піти не так.
jimmij

@jimmij ах, я бачу. Це може бути корисно, якщо ви встановите попередньо попередження про проблеми, тому що я думаю, що це не так відомо, що це може статися. В іншому випадку я прийняв би вашу відповідь у випадках, коли повторювані запуски включають старий файл та Anthon для випадків, коли його слід перезаписати.
користувач121391

Відповіді:


12

Ви можете запобігти тому, щоб файл не xargsвикористовував:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1

Щоб запобігти проблемам з іменем файлів, які містять пробіли чи нові рядки, лапки або косої риски, я б використовував:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\0' |
  xargs -r0 shasum -- > sums.sha1

замість цього.

Потрібно --уникати проблем з іменами файлів, які починаються з -. Однак це не допоможе для файлу, який називається -. Якби ви використовували -print0замість цього -printf '%P\0', вам не знадобився --б і не виникли б проблеми з -файлом.


Ваше рішення - це те, що я в кінцевому підсумку використав. Мені особливо подобається, що наступні запуски не повторно перезавантажують файл контрольної суми та не надувають каталог. Також у своєму сценарії я використовував basenameназву файлу sums.sha1 із заданого повного шляху (це не було включено у запитання, але це може допомогти іншим).
користувач121391

7

Оскільки ви користуєтесь -maxdepth 1, я припускаю, що ви не хочете рекурсії. Якщо так, просто зробіть це в оболонці:

for f in ~/test/*; do
    shasum -- "$f"
done > sums.sha1

Щоб пропустити каталоги, ви можете:

for f in ~/test/*; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Якщо вам потрібна рекурсія і ви використовуєте bash, виконайте:

shopt -s globstar
for f in ~/test/**; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

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


Я думаю, ви б згадали, що це вирішує будь-які проблеми, з якими ОП матиме імена файлів з новими рядками в них. З іншого боку, якщо sums.sha1вже існує (з попереднього запуску), ваше рішення включить його.
Антон

Вибачте, я раніше не уточнював: maxdepth використовувався лише в цьому прикладі, я використовую функцію, де користувач / сценарій може надавати будь-які значення, хоча наразі мені потрібна лише глибина 1.
user121391

@ user121391 див. оновлену відповідь для рекурсивного підходу.
terdon

Зауважте, що він також намагатиметься перевірити суму інших типів нестандартних файлів, таких як труби, пристрої ... (і посилання на них).
Стефан Шазелас

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

4

з zsh:

shasum -- *(D.) > sums.sha1

Глобус буде розширений до того, як буде зроблено перенаправлення, тому sums.sha1заголовок не буде включено, якщо його там не було в першу чергу.

Dполягає в тому, щоб включити дот-файли (приховані файли) як findби. .полягає у виборі лише звичайних файлів (наприклад, ваших -type f).

У sums.sha1будь-якому разі виключити, якщо він був там у першу чергу:

setopt extendedglob # best in ~/.zshrc
shasum -- ^sums.sha1(D.) > sums.sha1

Зауважте, що вони виконують одну команду шасума, тож ви можете побачити помилку "Аргумент задовгий", якщо список величезний. Щоб обійти це:

autoload zargs
zargs -e/ -- *(D.) / shasum > sums.sha1

Я рекомендую використовувати ./*замість цього, *щоб уникнути можливих проблем з файлом, який називається -.


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

1

Як було сказано в інших відповідях, проблема полягає в тому, що оболонка відкривається і створює sums.sha1файл, перш ніж виконати ваш конвеєр. Ви можете використовувати програму, spongeяка є частиною moreutilsпакету багатьох дистрибутивів. На відміну від оболонки перенаправлення spongeбуде чекати, поки воно отримає все, перш ніж відкрити файл. Зазвичай він використовується, коли ви хочете записати прочитаний файл у тому ж конвеєрі.

У вашому випадку він використовується таким чином:

$ find -maxdepth 1 -type f -printf '%P\n' |xargs shasum |sponge sums.sha1
$ cat sums.sha1
31836aeaab22dc49555a97edb4c753881432e01d  B
7d157d7c000ae27db146575c08ce30df893d3a64  A

0

В якості альтернативи знаходженню / xargs тощо ви можете захотіти sha1deep. Це, мабуть, інший пакет - на моєму коробці він знаходиться в пакеті md5deep.

Як говорили інші, sums.sha1 створюється оболонкою ще до початку пошуку. Трюк з ! -name sums.sha1до findбуде працювати, як буде

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.