Jak mogę sprawdzić, czy plik można utworzyć lub skrócić / zastąpić w bash?

15

Użytkownik wywołuje mój skrypt ze ścieżką do pliku, która zostanie utworzona lub nadpisana w pewnym momencie skryptu, np . foo.sh file.txtLub foo.sh dir/file.txt.

Zachowanie tworzenia lub zastępowania przypomina wymagania dotyczące umieszczenia pliku po prawej stronie >wyjściowego operatora przekierowania lub przekazania go jako argumentu tee(w rzeczywistości przekazanie go jako argumentu dotee jest dokładnie tym, co robię ).

Zanim przejdę do sedna skryptu, chcę w rozsądny sposób sprawdzić, czy plik można utworzyć / zastąpić, ale tak naprawdę go nie tworzyć. Ta kontrola nie musi być idealna i tak, zdaję sobie sprawę, że sytuacja może się zmienić między sprawdzeniem a momentem, w którym plik jest rzeczywiście zapisywany - ale tutaj wszystko jest w porządku z najlepszym wysiłkiem rozwiązaniem typu , dzięki czemu mogę wcześnie wyskoczyć w przypadku, gdy ścieżka do pliku jest nieprawidłowa.

Przykłady powodów, dla których nie udało się utworzyć pliku:

  • plik zawiera element katalogu, na przykład, dir/file.txtale katalog dirnie istnieje
  • użytkownik nie ma uprawnień do zapisu w określonym katalogu (lub CWD, jeśli nie określono katalogu

Tak, zdaję sobie sprawę, że sprawdzanie uprawnień „z góry” to nie jest UNIX Way ™ , raczej powinienem po prostu spróbować wykonać operację i poprosić o wybaczenie później. W moim konkretnym skrypcie prowadzi to jednak do złego komfortu użytkowania i nie mogę zmienić odpowiedzialnego komponentu.

BeeOnRope
źródło
Czy argument zawsze będzie oparty na bieżącym katalogu, czy może użytkownik określi pełną ścieżkę?
jesse_b
@Jesse_b - Przypuszczam, że użytkownik może podać bezwzględną ścieżkę, taką jak /foo/bar/file.txt. Zasadniczo mijam ścieżkę teepodoba tee $OUT_FILEgdzie OUT_FILEjest przekazywane w linii poleceń. To powinno „po prostu działać” na ścieżkach absolutnych i względnych, prawda?
BeeOnRope
@BeeOnRope, nie potrzebujesz tee -- "$OUT_FILE"przynajmniej. Co jeśli plik już istnieje lub istnieje, ale nie jest zwykłym plikiem (katalog, dowiązanie symboliczne, fifo)?
Stéphane Chazelas
@ StéphaneChazelas - cóż, używam tee "${OUT_FILE}.tmp". Jeśli plik już istnieje, teezastępuje, co w tym przypadku jest pożądanym zachowaniem. Jeśli to katalog, teezawiedzie (tak myślę). symlink Nie jestem w 100% pewien?
BeeOnRope

Odpowiedzi:

11

Oczywistym testem byłoby:

if touch /path/to/file; then
    : it can be created
fi

Ale tak naprawdę tworzy plik, jeśli jeszcze go nie ma. Możemy po sobie posprzątać:

if touch /path/to/file; then
    rm /path/to/file
fi

Spowoduje to jednak usunięcie istniejącego pliku, którego prawdopodobnie nie chcesz.

Mamy jednak możliwość obejścia tego:

if mkdir /path/to/file; then
    rmdir /path/to/file
fi

Nie możesz mieć katalogu o takiej samej nazwie jak inny obiekt w tym katalogu. Nie mogę wymyślić sytuacji, w której można utworzyć katalog, ale nie utworzyć pliku. Po tym teście skrypt będzie mógł swobodnie tworzyć konwencjonalne /path/to/filei robić z nim, co tylko zechce.

DopeGhoti
źródło
2
To bardzo dobrze radzi sobie z tak wieloma problemami! Komentarz do kodu wyjaśniający i uzasadniający to niezwykłe rozwiązanie może znacznie przekraczać długość odpowiedzi. :-)
jpaugh
Użyłem również if mkdir /var/run/lockdirjako testu blokującego. Jest to atomowe, choć if ! [[ -f /var/run/lockfile ]]; then touch /var/run/lockfilema krótki przedział czasu między testem a momentem rozpoczęcia touchinnej instancji danego skryptu.
DopeGhoti,
8

Z tego, co zbieram, chcesz to sprawdzić podczas używania

tee -- "$OUT_FILE"

(zwróć uwagę, --że inaczej nie zadziała w przypadku nazw plików rozpoczynających się od -), teeudałoby się otworzyć plik do zapisu.

To jest to:

  • długość ścieżki do pliku nie przekracza limitu PATH_MAX
  • plik istnieje (po rozpoznaniu dowiązania symbolicznego) i nie ma katalogu typu i masz do niego uprawnienia do zapisu.
  • jeśli plik nie istnieje, nazwa katalogu pliku istnieje (po rozpoznaniu dowiązania symbolicznego) jako katalog i masz do niego uprawnienia do zapisu i wyszukiwania, a długość nazwy pliku nie przekracza limitu NAME_MAX systemu plików, w którym znajduje się katalog.
  • lub plik jest dowiązaniem symbolicznym, który wskazuje na plik, który nie istnieje i nie jest pętlą dowiązania symbolicznego, ale spełnia powyższe kryteria

Na razie zignorujemy systemy plików takie jak vfat, ntfs lub hfsplus, które mają ograniczenia co do tego, jakie nazwy plików mogą zawierać wartości bajtów, przydział dysku, limit procesów, selinux, apparmor lub inny mechanizm bezpieczeństwa w sposób, pełny system plików, brak i-węzła, urządzenie pliki, których nie można otworzyć w ten sposób z tego czy innego powodu, pliki wykonywalne aktualnie zamapowane w jakiejś przestrzeni adresowej procesu, z których wszystkie mogą również wpłynąć na możliwość otwarcia lub utworzenia pliku.

Z zsh:

zmodload zsh/system
tee_would_likely_succeed() {
  local file=$1 ERRNO=0 LC_ALL=C
  if [ -d "$file" ]; then
    return 1 # directory
  elif [ -w "$file" ]; then
    return 0 # writable non-directory
  elif [ -e "$file" ]; then
    return 1 # exists, non-writable
  elif [ "$errnos[ERRNO]" != ENOENT ]; then
    return 1 # only ENOENT error can be recovered
  else
    local dir=$file:P:h base=$file:t
    [ -d "$dir" ] &&    # directory
      [ -w "$dir" ] &&  # writable
      [ -x "$dir" ] &&  # and searchable
      (($#base <= $(getconf -- NAME_MAX "$dir")))
    return
  fi
}

W bashdowolnej powłoce podobnej do Bourne'a po prostu wymień

zmodload zsh/system
tee_would_likely_succeed() {
  <zsh-code>
}

z:

tee_would_likely_succeed() {
  zsh -s -- "$@" << 'EOF'
  zmodload zsh/system
  <zsh-code>
EOF
}

Dostępne zshsą tutaj specyficzne funkcje $ERRNO(które ujawniają kod błędu ostatniego wywołania systemowego) i $errnos[]tablica asocjacyjna do przetłumaczenia na odpowiednie standardowe nazwy makr C. I $var:h(z csh) i$var:P (potrzebuje Zsh 5.3 lub nowszej wersji).

bash nie ma jeszcze równoważnych funkcji.

$file:hmożna zastąpić dir=$(dirname -- "$file"; echo .); dir=${dir%??}lub GNU dirname:IFS= read -rd '' dir < <(dirname -z -- "$file") .

Na $errnos[ERRNO] == ENOENT, podejście może być prowadzonyls -Ld pliku i sprawdzenia, czy komunikat o błędzie odpowiada błędowi ENOENT. Robienie tego niezawodnie i przenośnie jest jednak trudne.

Jednym podejściem może być:

msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}

(zakładając, że komunikat o błędzie kończy się syserror()tłumaczeniem ENOENTi że to tłumaczenie nie zawiera a :), a następnie zamiast [ -e "$file" ]:

err=$(ls -Ld -- "$file" 2>&1)

I sprawdź ENOENT błąd z

case $err in
  (*:"$msg_for_ENOENT") ...
esac

Ta $file:Pczęść jest najtrudniejsza do osiągnięcia bash, szczególnie na FreeBSD.

FreeBSD ma realpathpolecenie i readlinkpolecenie, które akceptuje -fopcję, ale nie można ich użyć w przypadkach, gdy plik jest dowiązaniem symbolicznym, który nie jest rozpoznawany. To samo z perl„s” Cwd::realpath().

python„s os.path.realpath()wydaje się działać podobnie do zsh $file:P, więc przy założeniu, że co najmniej jedna wersja pythonjest zainstalowana i że jest pythonpolecenie, które odnosi się do jednego z nich, można zrobić (co nie jest podane na FreeBSD):

dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}

Ale równie dobrze możesz zrobić wszystko python.

Lub możesz zdecydować, aby nie zajmować się tymi wszystkimi przypadkami narożnymi.

Stéphane Chazelas
źródło
Tak, poprawnie zrozumiałeś moją intencję. W rzeczywistości pierwotne pytanie było niejasne: pierwotnie mówiłem o tworzeniu pliku, ale tak naprawdę go używam, teewięc wymaganie jest takie, aby plik mógł zostać utworzony, jeśli nie istnieje, lub może zostać skrócony do zera i zastąpiony jeśli tak (lub jakkolwiek to teeobsługuje).
BeeOnRope
Wygląda na to, że Twoja ostatnia edycja wyczyściła formatowanie
D. Ben Knoble,
Dzięki, @ bishop, @ D.BenKnoble, patrz edycja.
Stéphane Chazelas,
1
@DennisWilliamson, cóż, tak, powłoka jest narzędziem do wywoływania programów, a każdy program, który wywołujesz w skrypcie, musi być zainstalowany, aby skrypt działał, co jest oczywiste. macOS ma domyślnie zainstalowane zarówno bash, jak i zsh. FreeBSD nie zawiera żadnego z nich. OP może mieć dobry powód, aby chcieć to zrobić w bash, być może jest to coś, co chcą dodać do już napisanego skryptu bash.
Stéphane Chazelas,
1
@pipe znowu powłoka jest interpretatorem poleceń. Zobaczysz, że wiele fragmentów Shellcode napisane invoke tłumaczy innych języków takich jak perl, awk, sedlub python(lub nawet sh, bash...). Skrypty powłoki polegają na użyciu najlepszej komendy dla tego zadania. Tutaj nawet perlnie ma odpowiednika zsh's $var:P( Cwd::realpathzachowuje się jak BSD realpathlub readlink -fgdy chcesz, aby zachowywał się jak GNU readlink -flub python' s os.path.realpath())
Stéphane Chazelas
6

Jedną z opcji, którą warto rozważyć, jest wcześniejsze utworzenie pliku, ale wypełnienie go później w skrypcie. Możesz użyć execpolecenia, aby otworzyć plik w deskryptorze pliku (takim jak 3, 4 itd.), A następnie użyć przekierowania do deskryptora pliku (>&3 itp.), Aby zapisać zawartość do tego pliku.

Coś jak:

#!/bin/bash

# Open the file for read/write, so it doesn't get
# truncated just yet (to preserve the contents in
# case the initial checks fail.)
exec 3<>dir/file.txt || {
    echo "Error creating dir/file.txt" >&2
    exit 1
}

# Long checks here...
check_ok || {
    echo "Failed checks" >&2
    # cleanup file before bailing out
    rm -f dir/file.txt
    exit 1
}

# We're ready to write, first truncate the file.
# Use "truncate(1)" from coreutils, and pass it
# /dev/fd/3 so the file doesn't need to be reopened.
truncate -s 0 /dev/fd/3

# Now populate the file, use a redirection to write
# to the previously opened file descriptor.
populate_contents >&3

Możesz także użyć trap do wyczyszczenia pliku po błędzie, co jest powszechną praktyką.

W ten sposób otrzymujesz prawdziwe sprawdzenie uprawnień, które będziesz w stanie utworzyć plik, a jednocześnie będziesz w stanie wykonać go wystarczająco wcześnie, aby w razie niepowodzenia nie spędzać czasu na czekaniu.


ZAKTUALIZOWANO: Aby uniknąć zablokowania pliku w przypadku niepowodzenia sprawdzeń, użyj fd<>fileprzekierowania bash, które nie powoduje natychmiastowego obcięcia pliku . (Nie zależy nam na czytaniu z pliku, jest to tylko obejście, więc go nie obcinamy.>> prawdopodobnie też po prostu zadziałałoby, ale wydaje mi się, że ten jest nieco bardziej elegancki, utrzymując flagę O_APPEND poza zdjęcia.)

Kiedy jesteśmy gotowi do zastąpienia zawartości, musimy najpierw obciąć plik (w przeciwnym razie, jeśli piszemy mniej bajtów niż w pliku wcześniej, końcowe bajty pozostaną tam.) Możemy użyć skrótu (1) w tym celu polecenie z coreutils, i możemy przekazać mu otwarty deskryptor pliku, który mamy (używając /dev/fd/3pseudopliku), więc nie trzeba go ponownie otwierać. (Znowu technicznie coś prostszego : >dir/file.txtprawdopodobnie by działało, ale nie trzeba ponownie otwierać pliku to bardziej eleganckie rozwiązanie).

filbranden
źródło
Zastanawiałem się nad tym, ale problem polega na tym, że jeśli jakiś inny niewinny błąd wystąpi gdzieś pomiędzy momentem tworzenia pliku a momentem, w którym później go napiszę, użytkownik prawdopodobnie będzie zdenerwowany, gdy dowie się, że określony plik został zastąpiony .
BeeOnRope
1
@BeeOnRope Inną opcją, która nie spowoduje zablokowania pliku, jest próba dodania do niego niczego : echo -n >>filelub true >> file. Oczywiście ext4 ma pliki tylko do dołączania, ale możesz żyć z tym fałszywie dodatnim.
mosvy
1
@BeeOnRope Jeśli chcesz zachować (a) istniejący plik lub (b) (kompletny) nowy plik, to jest inne pytanie niż zadałeś. Dobrą odpowiedzią jest przeniesienie istniejącego pliku do nowej nazwy (np. my-file.txt.backup) Przed utworzeniem nowego. Innym rozwiązaniem jest zapisanie nowego pliku do pliku tymczasowego w tym samym folderze, a następnie skopiowanie go do starego pliku po pomyślnym zakończeniu reszty skryptu --- jeśli ostatnia operacja się nie powiedzie, użytkownik może ręcznie naprawić problem bez utraty jego postęp.
jpaugh
1
@BeeOnRope Jeśli wybierzesz trasę „wymiany atomowej” (która jest dobra!), Zwykle będziesz chciał zapisać plik tymczasowy w tym samym katalogu co plik końcowy (aby zmienić nazwę, musi być w tym samym systemie plików) (2), aby odnieść sukces) i użyć unikalnej nazwy (więc jeśli uruchomione są dwa wystąpienia skryptu, nie będą się wzajemnie blokować plików tymczasowych.) Otwarcie pliku tymczasowego w celu zapisu w tym samym katalogu jest zwykle dobrym wskazaniem Będę mógł później zmienić jego nazwę (cóż, chyba że ostateczny cel istnieje i jest katalogiem), więc do pewnego stopnia odpowiada również na twoje pytanie.
filbranden
1
@jpaugh - nie chcę tego robić. Nieuchronnie doprowadziłoby to do odpowiedzi próbujących rozwiązać mój problem wyższego poziomu, co może dopuszczać rozwiązania, które nie mają nic wspólnego z pytaniem „Jak mogę sprawdzić ...”. Niezależnie od tego, na czym polega mój problem na wysokim poziomie, myślę, że to pytanie jest samodzielne jako coś, co można rozsądnie chcieć zrobić. Byłoby niesprawiedliwe dla osób, które odpowiadają na to pytanie, gdybym zmienił je na „rozwiązać ten konkretny problem, który mam ze swoim skryptem”. Podałem te szczegóły tutaj, ponieważ zostałem poproszony, ale nie chcę stawiać zmiany w podstawowym pytaniu.
BeeOnRope
4

Myślę, że rozwiązanie DopeGhoti jest lepsze, ale powinno również działać:

file=$1
if [[ "${file:0:1}" == '/' ]]; then
    dir=${file%/*}
elif [[ "$file" =~ .*/.* ]]; then
    dir="$(PWD)/${file%/*}"
else
    dir=$(PWD)
fi

if [[ -w "$dir" ]]; then
    echo "writable"
    #do stuff with writable file
else
    echo "not writable"
    #do stuff without writable file
fi

Pierwsza konstrukcja if sprawdza, czy argument jest pełną ścieżką (zaczyna się od /) i ustawia dirzmienną na ścieżkę katalogu do ostatniej /. W przeciwnym razie, jeśli argument nie zaczyna się od, /ale zawiera /(określając podkatalog), ustawi dirna bieżący katalog roboczy + ścieżkę do podkatalogu. W przeciwnym razie zakłada bieżący katalog roboczy. Następnie sprawdza, czy ten katalog jest zapisywalny.

jesse_b
źródło
4

Co powiesz na użycie normalnego testpolecenia, jak opisano poniżej?

FILE=$1

DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'

if [ -d $DIR ] ; then
  echo "base directory $DIR for file exists"
  if [ -e $FILE ] ; then
    if [ -w $FILE ] ; then
      echo "file exists, is writeable"
    else
      echo "file exists, NOT writeable"
    fi
  elif [ -w $DIR ] ; then
    echo "directory is writeable"
  else
    echo "directory is NOT writeable"
  fi
else
  echo "can NOT create file in non-existent directory $DIR "
fi
Jaleks
źródło
Prawie zduplikowałem tę odpowiedź! Przyjemne wprowadzenie, ale możesz wspomnieć man test, i to [ ]jest równoważne testowi (już nie jest to powszechna wiedza). Oto jedno-linijka, której miałem użyć w mojej gorszej odpowiedzi, aby objąć dokładny przypadek, dla potomności:if [ -w $1] && [ ! -d $1 ] ; then echo "do stuff"; fi
Iron Gremlin
4

Wspomniałeś, że wrażenia użytkownika kierują Twoim pytaniem. Odpowiem pod kątem UX, ponieważ masz dobre odpowiedzi od strony technicznej.

Zamiast przeprowadzania kontroli z góry, co powiesz na zapisanie wyników do pliku tymczasowego, a następnie na samym końcu, umieszczenie wyników w żądanym pliku użytkownika? Lubić:

userfile=${1:?Where would you like the file written?}
tmpfile=$(mktemp)

# ... all the complicated stuff, writing into "${tmpfile}"

# fill user's file, keeping existing permissions or creating anew
# while respecting umask
cat "${tmpfile}" > "${userfile}"
if [ 0 -eq $? ]; then
    rm "${tmpfile}"
else
    echo "Couldn't write results into ${userfile}." >&2
    echo "Results available in ${tmpfile}." >&2
    exit 1
fi

Zaleta tego podejścia: zapewnia pożądaną operację w normalnym scenariuszu szczęśliwej ścieżki, omija kwestię testowania i ustawiania atomowości, zachowuje uprawnienia do pliku docelowego podczas tworzenia, jeśli to konieczne, i jest bardzo prosta do wdrożenia.

Uwaga: gdybyśmy to wykorzystali mv, zachowalibyśmy uprawnienia do pliku tymczasowego - myślę, że tego nie chcemy: chcemy zachować uprawnienia ustawione w pliku docelowym.

Teraz źle: wymaga podwójnej przestrzeni ( cat .. >konstrukcji), zmusza użytkownika do wykonania pracy ręcznej, jeśli plik docelowy nie był zapisywalny w tym momencie, w którym powinien być, i pozostawia plik tymczasowy leżący (co może mieć zabezpieczenia lub problemy konserwacyjne).

biskup
źródło
W rzeczywistości jest to mniej więcej to, co teraz robię. Zapisuję większość wyników do pliku tymczasowego, a następnie na koniec wykonuję końcowy etap przetwarzania i zapisuję wyniki do pliku końcowego. Problemem jest to, że chcę wyskoczyć wcześnie (na początku skryptu), jeśli ten ostatni krok może się nie powieść. Skrypt może działać bez nadzoru przez kilka minut lub godzin, więc naprawdę chcesz wiedzieć z góry, że jest skazany na niepowodzenie!
BeeOnRope,
Jasne, ale istnieje wiele sposobów, które mogą się nie powieść: dysk może się zapełnić, katalog nadrzędny może zostać usunięty, uprawnienia mogą się zmienić, plik docelowy może być użyty do innych ważnych rzeczy, a użytkownik zapomniał, że przypisał ten sam plik do zniszczenia przez to operacja. Jeśli mówimy o tym z czystego punktu widzenia UX, być może właściwą rzeczą jest potraktowanie go jak przesłanie pracy: na końcu, jeśli wiesz, że działało poprawnie do końca, po prostu powiedz użytkownikowi, gdzie znajduje się wynikowa treść i zaoferuj zaproponował polecenia dla nich, aby przenieść go sami.
biskup
Teoretycznie tak, istnieją nieskończone sposoby, by to mogło zawieść. W praktyce przeważająca większość przypadków kończy się niepowodzeniem, ponieważ podana ścieżka jest nieprawidłowa. Nie mogę w uzasadniony sposób uniemożliwić niektórym nieuczciwym użytkownikom jednoczesnego modyfikowania FS, aby przerwać zadanie w trakcie operacji, ale z pewnością mogę sprawdzić przyczynę niepowodzenia nr 1 nieprawidłowej lub niemożliwej do zapisania ścieżki.
BeeOnRope
2

TL; DR:

: >> "${userfile}"

W przeciwieństwie do <> "${userfile}"lub touch "${userfile}", nie spowoduje to żadnych fałszywych modyfikacji znaczników czasu pliku, a także będzie działać z plikami tylko do zapisu.


Z PO:

Chcę rozsądnie sprawdzić, czy plik można utworzyć / zastąpić, ale tak naprawdę go nie tworzyć.

I od twojego komentarza do mojej odpowiedzi z perspektywy UX :

Przytłaczająca większość czasu, gdy się to nie udaje, wynika z tego, że podana ścieżka jest nieprawidłowa. Nie mogę racjonalnie uniemożliwić niektórym nieuczciwym użytkownikom jednoczesnego modyfikowania FS w celu przerwania zadania w trakcie operacji, ale z pewnością mogę sprawdzić przyczynę niepowodzenia nr 1 nieprawidłowej lub niemożliwej do zapisania ścieżki.

Jedynym wiarygodnym testem jest open(2)plik, ponieważ tylko to rozwiązuje każde pytanie dotyczące możliwości zapisu: ścieżki, własności, systemu plików, sieci, kontekstu bezpieczeństwa itp. Każdy inny test dotyczy pewnej części możliwości zapisu, ale nie innych. Jeśli chcesz mieć podzbiór testów, ostatecznie musisz wybrać to, co jest dla Ciebie ważne.

Ale oto kolejna myśl. Z tego co rozumiem:

  1. proces tworzenia treści jest długotrwały, oraz
  2. plik docelowy należy pozostawić w spójnym stanie.

Chcesz zrobić to sprawdzenie wstępne z powodu # 1 i nie chcesz majstrować przy istniejącym pliku z powodu # 2. Dlaczego więc nie poprosisz powłoki o otwarcie pliku do dołączenia, ale tak naprawdę niczego nie dołączasz?

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

$ for p in file_r dir_r/foo file_w dir_w/foo; do : >> $p; done
-bash: file_r: Permission denied
-bash: dir_r/foo: Permission denied

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
   └── [-rw-rw-r--           0]  foo
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

Pod maską rozwiązuje to kwestię zapisu dokładnie tak, jak chciał:

open("dir_w/foo", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

ale bez modyfikowania zawartości pliku lub metadanych. Teraz tak, to podejście:

  • nie informuje, czy plik jest tylko dołączany, co może stanowić problem, gdy przejdziesz do jego aktualizacji po zakończeniu tworzenia treści. Możesz to wykryć do pewnego stopnia lsattri odpowiednio zareagować.
  • tworzy plik, który wcześniej nie istniał, w takim przypadku: złagodź to za pomocą wybiórczego rm.

Chociaż twierdzę (w mojej innej odpowiedzi), że najbardziej przyjazne dla użytkownika podejście jest utworzenie pliku tymczasowego, który użytkownik musi przenieść, myślę, że jest to podejście najmniej przyjazne dla użytkownika, aby w pełni zweryfikować ich dane wejściowe.

biskup
źródło