Jak to zrobić poprawnie
Przede wszystkim zawsze podawaj swoje zmienne . To, co próbujesz zrobić, działa dobrze, jeśli odpowiednio go zacytujesz:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
Zachowałem dziwną nazwę pliku, którą wybrałeś ( chociaż nie mam pojęcia, dlaczego ją wybrałeś ) dla zachowania spójności.
Teraz przypiszmy ścieżkę pullingATerdon
do zmiennej, a następnie spróbuj otworzyć plik:
$ bacon="$(realpath pullingATerdon)"
$ echo "$bacon"
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]/pullingATerdon
$ ls $bacon
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
To się nie powiedzie, zgodnie z oczekiwaniami. Ale jeśli teraz podamy to poprawnie:
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
Działa zgodnie z oczekiwaniami. I tak, możesz także otworzyć ścieżkę w (właściwym) edytorze: emacs "$bacon"
będzie działać dobrze. OK, podobnie będzie vim
i cokolwiek innego. Twój wybór redaktora, choć niefortunny, nie ma znaczenia.
Dlaczego twój zawiódł
Szybkim sposobem na prześledzenie, co faktycznie się wydarzyło w twoim przypadku, jest użycie set -x
(wyłączenie go ponownie za pomocą set +x
), co powoduje, że powłoka wypisuje każde polecenie, które uruchomi przed uruchomieniem. włącz komunikaty debugowania powłoki za pomocą set -x
:
$ set -x
$ /bin/ls $bacon
+ ls '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
ls: cannot access '+'\''|'\''|_e|\|\|0rth': No such file or directory
ls: cannot access '[`-_-"]/pullingATerdon': No such file or directory
'/home/terdon/foo/\e[92mM@r|<':
To pokazuje nam, że ls
został uruchomiony z trzech oddzielnych argumenty '/home/terdon/foo/\e[92mM@r|<'
, '+'\''|'\''|_e|\|\|0rth'
i '[`-_-"]/pullingATerdon'
. Dzieje się tak, ponieważ powłoka wykonuje dzielenie słów i rozwijanie globu na ciągach niecytowanych. W tym przypadku problemem jest dzielenie słów, ponieważ powłoka widziała spacje na ścieżce i odczytywała każdy ciąg oddzielony spacją jako osobny argument.
mkdir
Przykładem jest nieco inna, ale to dlatego, że jesteś pokazując nam się komunikat o błędzie z drugim wywołaniu polecenia. Wydaje mi się, że raz go wypróbowałeś, a następnie uruchomiłeś go po raz drugi, aby uzyskać wynik swojego pytania. Przy pierwszym uruchomieniu wyglądałby tak:
$ mkdir $(realpath pullingATerdon)
++ realpath pullingATerdon
+ mkdir '/home/terdon/foo/\e[92mM@r|<' '+'\''|'\''|_e|\|\|0rth' '[`-_-"]/pullingATerdon'
mkdir: cannot create directory ‘[`-_-"]/pullingATerdon’: No such file or directory
Ponownie spróbuje utworzyć trzy katalogi, a nie jeden, z powodu podziału słów. Najpierw utworzył (z powodzeniem) katalog /home/terdon/foo/\e[92mM@r|<
:
$ ls -l /home/terdon/foo/
total 8
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|<'
drwxr-xr-x 3 terdon terdon 4096 Mar 15 00:20 '\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]'
Następnie również pomyślnie utworzył katalog o nazwie +'|'|_e|\|\|0rth
w bieżącym katalogu:
$ ls -l
total 4
drwxr-xr-x 2 terdon terdon 4096 Mar 15 00:37 '+'\''|'\''|_e|\|\|0rth'
-rw-r--r-- 1 terdon terdon 0 Mar 15 00:36 pullingATerdon
A następnie próbował utworzyć katalog [`-_-"]/pullingATerdon
. Nie powiodło się, ponieważ mkdir
domyślnie nie tworzy podkatalogów (może, jeśli go uruchomisz -p
):
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
Ponieważ ciąg niecytowany zawierał /
, mkdir
uważany za ścieżkę dwóch katalogów, próba znalezienia pierwszego i nie powiodła się.
Dlatego się nie udało, ale to, co się stało, jest bardziej skomplikowane. Ciąg użyłeś jest rzeczywiście glob powłoki, konkretnie zakres glob , który pasuje do wszystkich plików w bieżącym katalogu, którego nazwa jest jednym z 5 znaków `
, -
, _
lub "
. Ponieważ nie masz takich plików w bieżącym katalogu, glob nie pasuje do niczego i, podobnie jak domyślne zachowanie w bash, zwraca się:
$ echo "[\`-_-\"]/pullingATerdon" ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon' ## but it echoes the right thing
[`-_-"]/pullingATerdon ## and matches nothing, so returns itself.
Aby to wyjaśnić, oto co się stanie, jeśli podasz glob, który pasuje do czegoś:
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
Niecytowany [p*]
jest rozwijany do listy pasujących nazw plików (w tym przypadku tylko jednej) i do tego jest przekazywany echo
. To kolejny powód, dla którego powinieneś cytować wszystkie rzeczy.
Wreszcie rzeczywisty błąd, który wyświetlasz, pojawia się od drugiego uruchomienia polecenia i kończy się niepowodzeniem na pierwszym etapie podczas próby utworzenia /home/terdon/foo/\e[92mM@r|<
, ponieważ poprzednie wywołanie utworzyło już ten katalog.
Mówiąc bardziej ogólnie, za każdym razem, gdy pracujesz z dowolnymi nazwami plików, zawsze używaj globów powłoki. Takie rzeczy:
for file in *; do command "$file"; done
To zadziała dla dowolnej nazwy pliku. Bez względu na to, co się zawiera. W powyższym przykładzie mogłeś:
emacs /home/terdon/*92mM*/pullingATerdon
Wykona to każdy glob, który jednoznacznie identyfikuje plik docelowy. W ten sposób nie musisz się martwić o znaki specjalne i możesz po prostu pozwolić powłoce sobie z nimi poradzić.
Kilka przydatnych odniesień:
Jak mogę znaleźć i bezpiecznie obsługiwać nazwy plików zawierające znaki nowej linii, spacje lub oba? : Jedno z najczęściej zadawanych pytań na doskonałej Wiki Greya Cat.
Skutki bezpieczeństwa zapomnienia o cytowaniu zmiennej w powłokach bash / POSIX : ten sam post, do którego odwoływałem się na początku tej odpowiedzi. Świetne i bardzo szczegółowe wyjaśnienie wszystkich rzeczy, które mogą pójść nie tak, jeśli nie podasz poprawnie zmiennych zmiennych powłoki.
Dlaczego mój skrypt powłoki dusi się spacjami lub innymi znakami specjalnymi? : wszystko, co kiedykolwiek chciałeś wiedzieć o obsłudze dowolnych nazw plików w powłoce.
Kiedy konieczne jest podwójne cytowanie? : Więcej informacji o cudzysłowach i zmiennych, a szczególnie o kilku przypadkach, w których nie trzeba ich cytować
vim "$bacon"