Чи є спосіб зробити perl -i не клоберовими посиланнями?


12

Мій друг вказує, що якщо ви:

perl -pi.bak -e 's/foo/bar/' somefile

коли "somefile" насправді є символьним посиланням, perl робить саме те, що документи говорять, що буде робити:

Це робиться шляхом перейменування вхідного файлу, відкриття вихідного файла на оригінальне ім'я та вибору цього вихідного файла як типового для операторів print (). Розширення, якщо воно постачається, використовується для зміни імені старого файлу для створення резервної копії [...]

У результаті чого нове посилання "somefile.bak" вказує на незмінний реальний файл, а також новий, змінений звичайний файл "somefile" зі змінами.

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

( sedробить те саме, для чого це варто.)


1
Зателефонувати vim чи emacs (я думаю, обидва слідують за посиланнями)? Я серйозно боюся, що відповідь полягає у повторному виконанні -p -iу вашому сценарії.
Жил 'ТАК - перестань бути злим'

Насправді Sed не робить те саме, принаймні не з версії 4.2.1, випущеної в червні 2009 року. Для редагування файлу зв'язаним файлом ви повинні включити опцію --follow-symlinks для редагування файлу зв'язаного файлу, а не клобірування символьної посилання ; Я припускаю, що це було зроблено, щоб уникнути зламу існуючих сценаріїв, який може залежати від старої поведінки.
Гаррет

Відповіді:


6

Цікаво, чи допоможе в цьому випадку невелика spongeутиліта загального призначення ("всмоктувати стандартний вхід і записати у файл") з moreutils і чи буде вона слідувати за посиланням.

Автор описує sponge так:

Він вирішує проблему редагування файлів на місці за допомогою інструментів Unix, а саме: якщо ви просто перенаправляєте вихід у файл, який ви намагаєтесь відредагувати, то перенаправлення набуває чинності (клобування вмісту файла) перед першою командою в конвеєрі обходить читання з файлу. Комутатори подобаються sed -iі perl -iпрацюють навколо цього, але не кожна команда, яку ви хочете використовувати в конвеєрі, має такий варіант, і ви не можете використовувати такий підхід з конвеєрами з декількома командами.

Я зазвичай використовую губку трохи так:

sed '...' file | grep '...' | sponge file

Гей здорово, це працює. Я підозрюю, що коментар Жилла вище - це "справжня" відповідь, але оскільки він не зробив це як відповідь, і оскільки я дізнався нову утиліту, я візьму цю. :)
mattdm

Але ви протестували spongeна такому використанні на практиці? Щодо мене: поки що. Не могли б ви залишити коментар із зазначенням того, чи поводилось воно в тесті так, як тут хотілося? О, я бачу коментар. Дякуємо за підтвердження!
imz - Іван Захарящев

- так, я спробував це, перш ніж відповісти. Коли fileу вашому прикладі вище є посилання, посилання залишається в спокої і реальний файл змінюється.
mattdm

@mattdm: Так, дякую за підтвердження! (Я помітив ваші слова "що працює" трохи пізніше, ніж я написав свій коментар.)
imz - Іван Захарящев

3

Якщо ви знаєте факт, що somefileє символьним посиланням, ви можете явно надати повний шлях до файлу з наступним:

perl -pi.bak -e 's/foo/bar/' $(readlink somefile)

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


Але вихід readlinkможе бути ще одним символьним посиланням. Найкраще було б використовувати -fабо за -eнаявності, або класифікатор zsh's $file:Pабо глобус ', як показано @ikegami, щоб отримати шлях без символьних посилань . (:P)
Стефан Хазелас

3

Якщо ви маєте справу з одним файлом, команда виглядатиме так:

perl -i -pe'...' -- "$qfn"

Тоді рішення наступне:

perl -i -pe'...' -- "$( readlink -e -- "$qfn" )"

Це обробляє як символьні, так і несимвольні посилання.


Якщо ви маєте справу з довільно великою кількістю файлів, команда виглядатиме так:

... | xargs -r perl -i -pe'...' --

Тоді рішення наступне:

... | xargs -r readlink -ze -- | xargs -r0 perl -i -pe'...' --

Це обробляє як символьні, так і несимвольні посилання.


Caveat readlink - це не стандартна команда, тому її наявність, її аргументи та функціональність залежать від системи, тому обов'язково перевірте свою документацію. Наприклад, у деяких реалізацій є -f(які ви також можете використати тут), але ні -e, в деяких немає. busybox«S readlink -fповодиться як GNU readlink -e. А деякі системи мають realpathкоманду, замість readlinkякої взагалі поводиться як GNU readlink -f.

readlink -eБудьте уважні, що за допомогою GNU посилання, які неможливо вирішити до існуючого файлу, мовчки видаляються.


@ Stéphane Chazelas, $var:Pне працює для мене. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'виходи tmp:P)
ikegami

вам потрібно zsh 5.3 (2016) або вище для $var:P. З більш старими версіями ви завжди можете використовувати $var:Aзамість цього. Дивіться посібник щодо відмінностей та zsh.org/mla/users/2016/msg00593.html щодо обґрунтування введення :P(реальний realpath()інтерфейс).
Стефан Шазелас

( :Aдодано в 4.3.10 (2009), GNU readlink -zу 2012, я не знаю жодної іншої readlinkреалізації, яка підтримує -z). Зауважте, що з GNU xargsви, як правило, бажаєте цього -rваріанту, якщо не зможете гарантувати, що вхід не буде порожнім. Тут нічого не важливого (якщо perlкод не містить BEGIN/ ENDзаяви), ви просто отримаєте -i used with no filenames on the command line, reading from STDIN.попередження, якщо це станеться.
Стефан Шазелас

На жаль, я неправильно прочитав -rдокументацію. Я думав, що зробив протилежне тому, що робить. Прочитано.
ikegami
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.