Як це зробити правильно
Перш за все, завжди цитуйте свої змінні . Те, що ви намагаєтеся зробити, працює добре, якщо ви цитуєте це правильно:
$ pwd
/home/terdon/foo/\e[92mM@r|< +'|'|_e|\|\|0rth [`-_-"]
$ ls
pullingATerdon
Я зберіг дивне ім’я файлу, яке ви вибрали ( хоча я не маю уявлення, чому ви його вибрали ) заради послідовності.
Тепер давайте призначимо шлях pullingATerdon
до змінної, а потім спробуємо відкрити файл:
$ 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|<':
Це не вдається, як очікувалося. Але, якщо ми зараз це цитуємо правильно:
$ ls -l "$bacon"
-rw-r--r-- 1 terdon terdon 0 Mar 14 23:15 '/home/terdon/foo/\e[92mM@r|< +'\''|'\''|_e|\|\|0rth [`-_-"]/pullingATerdon'
Це працює як очікувалося. І так, ви також можете відкрити шлях у (належному) редакторі: emacs "$bacon"
буде добре працювати. Гаразд, так буде vim
і все інше. Ваш вибір редактора, хоч і невдалий, не має значення.
Чому ваш не вдався
Швидкий спосіб відстежити, що насправді сталося у вашому випадку, - це скористатися set -x
(вимкніть його знову set +x
), що призведе до того, що оболонка надрукує кожну команду, яку вона запустить, перш ніж запустити її. увімкніть повідомлення про налагодження оболонки за допомогою 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|<':
Це говорить про те , що ls
була запущена з трьома окремими аргументами: '/home/terdon/foo/\e[92mM@r|<'
, '+'\''|'\''|_e|\|\|0rth'
і '[`-_-"]/pullingATerdon'
. Це трапляється через те, що оболонка виконує розбиття слів та розширення глобулу на рядках, які не цитуються. У цьому випадку проблема полягає у розбитті слів, оскільки оболонка бачила пробіли на шляху та читала кожний розділений пробіл рядок як окремий аргумент.
mkdir
Приклад трохи відрізняється , але це тому , що ви показуєте нам повідомлення про помилку від другого виклику команди. Я думаю, ви спробували один раз, а потім запустили його вдруге, щоб отримати висновок для свого питання. Перший раз, коли ви його запустили, це виглядало б так:
$ 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
Знову ж таки, спробуємо створити три каталоги, не один, через розділення слів. По-перше, він створив (успішно) каталог /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 [`-_-"]'
Потім він, також успішно, створив каталог, названий +'|'|_e|\|\|0rth
у вашому поточному каталозі:
$ 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
А потім спробували створити каталог [`-_-"]/pullingATerdon
. Це не вдалося, оскільки mkdir
за замовчуванням не створюється підкаталогів (якщо це можна запустити -p
):
$ mkdir baz/bar
mkdir: cannot create directory ‘baz/bar’: No such file or directory
Оскільки ваш рядок без котирування містив a /
, mkdir
вважав, що шлях із двох каталогів, намагався знайти верхній і не вдався.
Тому не вдалося, але те, що сталося, є складнішим. Рядок, яка використовується на самому ділі оболонка Глоба, конкретно діапазон Glob , який відповідає всім файлам в поточному каталозі, ім'я якого один з 5 символів `
, -
, _
або "
. Оскільки у вашому поточному каталозі таких файлів немає, глобус нічого не відповідає і, як це поведінка за замовчуванням у bash, повертається сам:
$ echo "[\`-_-\"]/pullingATerdon" ## some escaping is needed here
+ echo '[`-_-"]/pullingATerdon' ## but it echoes the right thing
[`-_-"]/pullingATerdon ## and matches nothing, so returns itself.
Щоб уточнити, ось що відбувається, якщо ви подаруєте глобус, який щось відповідає:
$ echo [p]* ## any filename starting with a p
pullingATerdon
$ echo "[p]*" ## the string "[p]*"
[p]*
Без котирувань [p*]
розширюється до списку відповідних імен файлів (лише одне, в даному випадку), і саме це передається echo
. Ще одна причина, чому ви повинні цитувати всі речі.
Нарешті, фактична помилка, яку ви показуєте, відбувається з другого разу, коли ви запускаєте команду, і вона не вдається на першому кроці при спробі створення /home/terdon/foo/\e[92mM@r|<
, оскільки попереднє виклик вже створило цей каталог.
Більш загально, коли ви опинитесь на роботі з довільними іменами файлів, завжди використовуйте глобуси оболонки. Такі речі:
for file in *; do command "$file"; done
Це буде працювати для будь-якого імені файлу. Незалежно від того, що це буде містити. У нашому прикладі вище, ви могли:
emacs /home/terdon/*92mM*/pullingATerdon
Буде робити будь-який глобус, який однозначно ідентифікує цільовий файл. Таким чином, вам не потрібно турбуватися про спеціальні символи, і ви можете просто дозволити оболонці обробляти їх.
Деякі корисні посилання:
Як я можу знайти та безпечно обробити імена файлів, що містять нові рядки, пробіли чи обидва? : Один з найчастіших запитань про відмінну Віку Сірого Кота.
Вплив на безпеку забуття процитувати змінну в оболонках bash / POSIX : той самий пост, на який я посилався на початку цієї відповіді. Чудове і дуже детальне пояснення всіх речей, які можуть піти не так, якщо ви не зможете цитувати змінні оболонки правильно.
Чому мій сценарій оболонки задавлюється пробілом або іншими спеціальними символами? : все, що ви коли-небудь хотіли знати про обробку довільних імен файлів у оболонці.
Коли потрібно подвійне цитування? : Більше про лапки та змінні, а саме про декілька випадків, коли вам не потрібно цитувати їх
vim "$bacon"