In einem interaktiven Shell-Skript möchte ich die Ausgabe eines Befehls in einer Datei speichern, wie in
$ echo "Hello, World" > test.txt
aber verhindern, dass test.txt überschrieben wird, wenn es bereits existiert. Daher dachte ich, es wäre eine gute Idee,cpmit seiner interaktiven Option (cp -ich), um zu prüfen, ob die Zieldatei existiert. Ich habe Folgendes versucht:
$ echo -n "Hello" | cp -i /dev/stdin test.txt
Dies schreibt „Hallo“ in test.txt, wenn test.txt noch nicht existiert, bricht das Kopieren jedoch ab, wenn test.txt existiert, da cp die Antwort auf die Frage, ob überschrieben werden soll oder nicht, nur aus dieser Pipe liest.
Dies
$ cp -i <(echo "World") test.txt
cp: overwrite 'test.txt'? y
'/proc/self/fd/11' -> 'test.txt'
funktioniert wie vorgesehen, cp scheint den Dateideskriptor des Unterprozesses als Quelle zu nehmen und dieser wird in test.txt kopiert und ich weiß einfach nicht, warum.
Irgendwelche Ideen oder Erklärungen oder bessere Möglichkeiten, dies zu tun?
Antwort1
Verwenden Sie einen anderen Dateideskriptor anstelle von/dev/stdin
echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 </dev/tty; }
Oder weiterhin skriptfähig über yes(1)
{ echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 <&4; }; } 4<&0
In beiden Varianten werden die zusätzlichen { ... }
around cp -i ...
nur in zsh benötigt, um dessen nicht standardmäßigemultiosBesonderheit.
Dies sollte in jeder Standard-Shell und jedem System funktionieren, das Dateien unterstützt /dev/fd/N
, mit Ausnahme der Kombination Linux + ksh93, die nicht funktioniert, da ksh93 Pipes mit Unix-Sockets implementiert, die unter Linux nicht geöffnet werden können /dev/fd/N
(nicht einmal echo | cat /dev/stdin
funktionieren).
Sie echo -n "Hello" | cp -i /dev/stdin test.txt
werden die Datei tatsächlich zerstören, wenn Sie dort nichts anderes "Hello"
angeben "You suck!"
. Das ist einer der Gründe, warum Sie sich nicht auf verlassen sollten cp -i
.
Antwort2
Shells wie bash
können bereits vor dem Überschreiben von Dateien mit einer Umleitung schützen
set -o noclobber
echo hello >world
echo hello >world
-bash: world: cannot overwrite existing file
Wenn Sie den Benutzer vor dem Überschreiben fragen möchten, würde ich anstelle der noclobber
Einstellung so etwas verwenden
#!/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