Как я могу очистить или избежать абсолютных путей, возвращаемых realpath или readlink?

9

realpathи readlinkвернуть абсолютные пути:

+akiva@X230:~$ realpath ZannaIsAwesome
/home/akiva/ZannaIsAwesome

С таким путем легко справиться. Тем не менее, что-то вроде этого будет иметь некоторые проблемы:

введите описание изображения здесь

Например:

введите описание изображения здесь

Таким образом, имя, подобное этому, должно быть обработано, чтобы иметь возможность передавать его другим командам. Вариант использования может быть примерно таким:

+a@X230:~/\e[92mM@r|< $hu+'|'|_e|\|\|0rth [`-_-"]$ bacon=$(realpath pullingATerdon)
+a@X230:~$ vim $bacon 

Излишне говорить, vim $baconчто не будет работать, как ожидалось.

Что я могу сделать, чтобы очистить этот абсолютный путь, чтобы он работал с другими командами?

Акива
источник
3
Всегда указывайте свои переменные:vim "$bacon"
Steeldriver
@steeldriver Почему это?
Акива
3
Потому что, если вы этого не сделаете, вы попадете в такие ситуации, как эта :)
terdon

Ответы:

12

Как сделать это правильно

Прежде всего, всегда указывайте свои переменные . То, что вы пытаетесь сделать, работает нормально, если вы цитируете это правильно:

$ 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

Подойдет любой глобус, который однозначно идентифицирует целевой файл. Таким образом, вам не нужно беспокоиться о специальных символах, и вы можете просто позволить оболочке обрабатывать их.


Несколько полезных ссылок:

  1. Как я могу найти и безопасно обрабатывать имена файлов, содержащие переводы строк, пробелы или оба? : Один из часто задаваемых вопросов о превосходном Wiki Grey Cat.

  2. Последствия для безопасности: забыть процитировать переменную в оболочках bash / POSIX : тот же пост, на который я ссылался в начале этого ответа. Отличное и очень подробное объяснение всех вещей, которые могут пойти не так, если вам не удастся правильно процитировать ваши переменные оболочки.

  3. Почему мой сценарий оболочки задыхается от пробелов или других специальных символов? : все, что вы когда-либо хотели знать об обработке произвольных имен файлов в оболочке.

  4. Когда необходимо двойное цитирование? : Подробнее о кавычках и переменных и, в частности, о нескольких случаях, когда вам не нужно их заключать в кавычки

terdon
источник