Есть ли способ сделать Perl -i не клоббер символические ссылки?

12

Мой друг указывает, что если вы делаете:

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

когда «somefile» на самом деле является символической ссылкой, perl делает то, что говорят документы:

Это делается путем переименования входного файла, открытия выходного файла с исходным именем и выбора этого выходного файла в качестве операторов по умолчанию для print (). Расширение, если оно указано, используется для изменения имени старого файла, чтобы сделать резервную копию [...]

В результате получается новая символическая ссылка "somefile.bak", указывающая на неизмененный реальный файл, и новый, измененный обычный файл "somefile" с изменениями.

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

( sedделает то же самое, для чего это стоит.)

mattdm
источник
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
imz - Иван Захарящев
источник
Эй, круто, это работает. Я подозреваю, что приведенный выше комментарий Жиля является «реальным» ответом, но так как он не сделал его как ответ, и так как я изучил новую утилиту, я возьму этот. :)
mattdm
Но проверяли ли вы на практике spongeтакое использование? Что касается меня: пока нет. Не могли бы вы оставить комментарий о том, что он вел себя в тесте так, как хотелось бы здесь? О, я вижу комментарий. Спасибо за подтверждение!
imz - Иван Захарьящев
- да, я попробовал, прежде чем ответить. Когда fileв приведенном выше примере есть символическая ссылка, ссылка остается одна, и реальный файл изменился.
Mattdm
@mattdm: Да, спасибо за подтверждение! (Я заметил ваши слова «это работает» чуть позже, чем я написал свой комментарий.)
imz - Иван Захарящев
3

Если вы знаете, что somefileэто символическая ссылка, вы можете явно указать полный путь к файлу с помощью следующего:

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

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

IDDQD
источник
Но выводом readlinkможет быть еще одна символическая ссылка. Лучше всего было бы использовать -fили -eгде доступны или zsh«s $file:Pили (:P)шарик Классификатор , как показано на @ikegami , чтобы получить символическую ссылку свободного пробега.
Стефан Шазелас
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.

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

Ikegami
источник
@ Стефан Шазелас, $var:Pу меня не работает. ( X='tmp' zsh -c 'perl -E"say for @ARGV" $X:P'выходы tmp:P)
икегами
вам нужен 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документацию. Я думал, что сделал противоположное тому, что он делает. Readded.
икегами