В интерактивном скрипте оболочки я хотел бы сохранить вывод команды в файл, как в
$ echo "Hello, World" > test.txt
но не допустить перезаписи test.txt, если он уже существует. Поэтому я подумал, что было бы хорошей идеей использоватьсрс интерактивной опцией (ср -i) для проверки существования целевого файла. Я попробовал это:
$ echo -n "Hello" | cp -i /dev/stdin test.txt
Это записывает «Hello» в test.txt, если test.txt еще не существует, но прерывает копирование, если test.txt существует, поскольку cp считывает ответ на вопрос о том, перезаписывать или нет, только из этого канала.
Однако, это
$ cp -i <(echo "World") test.txt
cp: overwrite 'test.txt'? y
'/proc/self/fd/11' -> 'test.txt'
работает так, как и задумано, cp, похоже, берет filedesriptor подпроцесса в качестве источника и копирует его в test.txt, и я просто не знаю, почему.
Есть ли какие-нибудь идеи, объяснения или лучшие способы сделать это?
решение1
Используйте другой файловый дескриптор вместо/dev/stdin
echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 </dev/tty; }
Или, все еще можно написать скрипт через yes(1)
{ echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 <&4; }; } 4<&0
В обоих вариантах дополнительные { ... }
параметры cp -i ...
нужны только в zsh, чтобы обойти его нестандартность.мультиосособенность.
Это должно работать в любой стандартной оболочке и любой системе, которая поддерживает /dev/fd/N
файлы, за исключением комбинации Linux + ksh93, которая не будет работать, поскольку ksh93 реализует каналы с сокетами Unix, которые в Linux нельзя открыть /dev/fd/N
(даже не echo | cat /dev/stdin
будут работать).
Вы echo -n "Hello" | cp -i /dev/stdin test.txt
фактически затрете файл, если вместо этого "Hello"
вы поместите его "You suck!"
туда. Это одна из причин, почему вам не следует полагаться на cp -i
.
решение2
Такие оболочки, как bash
уже могут защищать от перезаписи файлов с помощью перенаправления
set -o noclobber
echo hello >world
echo hello >world
-bash: world: cannot overwrite existing file
Если вы хотите иметь возможность спрашивать пользователя перед перезаписью, я бы использовал что-то вроде этого вместо noclobber
настройки
#!/bin/bash
# Define function sureYN [<prompt>] <target>
sureYN() {
local prompt="$1" target="$2" yn='y'
[[ -z "$target" ]] && { target="$prompt"; prompt="Overwrite $target"; }
[[ -f "$target" && -t 0 ]] && read -p "$prompt (y/n)? " yn >&2
[[ "$yn" =~ ^[Yy] ]]
}
sureYN /tmp/first && cp -fp /etc/hosts /tmp/first
sureYN "Can I replace /tmp/second" /tmp/second && echo this is the second >/tmp/second