В довольно большом проекте я столкнулся со следующей проблемой: при вычислении правильной команды в bash-скрипте мне не удается заставить скрипт соблюдать двойную кавычку, необходимую для одиночных аргументов с пробелом.
$ ./testo "two three"
ls -l "two three_DD_FF" # <- Why does it look for two files HERE??
ls: cannot access '"two': No such file or directory
ls: cannot access 'three_DD_FF"': No such file or directory
«два три» — единственный аргумент.
С two_three мой код работает, как и следовало ожидать:
$ ./testo two_three
ls -l "two_three_DD_FF"
ls: cannot access '"two_three_DD_FF"': No such file or directory
И это основной общий код, который я мог бы придумать:
#!/usr/bin/bash
cmd = "ls -l"
trio = ""'"'"${1}_DD"
trio = "${cmd} ${trio}_FF"'"'""
echo ${trio}
${trio}
(Я не нашел менее странного метода, чем приведенный выше, для странно выглядящей сильной цитаты, но это не моя главная забота.)
Чего я не понимаю, так это проблемы с исполнением:
ls -l "two three_DD_FF"
в строке выше по-прежнему приводит к поиску двух файлов, несмотря на двойные кавычки; в то время как та же строка в командной строке выполняет свою работу по назначению (то есть ищет файл «two three_DD_FF"
:
$ ls -l "two three_DD_FF"
-rw-rw-r-- 1 myhome me 0 Aug 27 13:09 'two three_DD_FF'
То есть строка echo ${trio}
показывает правильную команду (по крайней мере, если она выполняется в командной строке; в то время как та же команда, если она выполняется из сценария, завершается неудачно. Она не учитывает двойные кавычки для имени файла, подлежащего поиску.
Мои вопросы:
Цитирование "two three"
помогает им быть одним аргументом в $1
, а не разделяться на $1, $2
. Но если вы затем запустите ls -l $1
, что, по сути, и делаете, поскольку $1
не заключен в кавычки, он будет разделен. Другими словами, кавычки не «сохраняются» в переменной. Вы это вроде уже понимаете, так как иначе по вашей логике cmd = "ls -l"
тоже не должно работать, так как не было бы разделения.
Вставьте свой скрипт на shellcheck.net для проверки/рекомендации. Имхо, никакая цитата не сможет исправить ваш сценарий.
Этот вопрос похож на: Почему оболочка игнорирует символы кавычек в аргументах, передаваемых ей через переменные?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.
Кавычки, заключенные в trio
, являются буквальными, а не синтаксическими, поэтому их нельзя использовать для предотвращения разделения слов.
Какой ответ вы получите, если нажмете type ls
?
Уточните: сценарий testso
, по которому вы звоните сюда, представляет собой странный небольшой сценарий bash, который вы разместили в своем вопросе?
Я голосую за закрытие этого вопроса, потому что автор не запустил свой скрипт сначала через shellcheck.net , как указано в теге bash.
То, как вы называете сценарий, имеет значение, что вы в нем пишете. Если вы напишете ${trio}
без кавычек, он всегда будет разделяться на пробелы, независимо от содержания. Вам придется прописать в скрипте конструкции, предотвращающие разбиение, но используя кавычки, которые вы пишете, а не генерируете, например ls -l "$trio"
. Вы не можете хранить команды в переменной — используйте функцию для хранения команд. Обязательно к прочтению mywiki.wooledge.org/BashFAQ/050 .
Спасибо за подсказку с массивами. И благодаря подсказке @Ed Morton. Однако закрывать вопрос, потому что кто-то (кто??) решил, что речь идет не о программировании, и голосовать против него, смешно. Сайт вопросов не должен отпугивать пользователей от вопросов, которые другие считают «слишком простыми» или что-то в этом роде.
К сожалению, не существует конкретной причины, по которой можно было бы нажать на ссылку «не следовал инструкциям тега», вам нужно ввести это вручную, и сайт просто выбирает одну из имеющихся у него групп, на которую можно нажать, чтобы отобразить ее в качестве причины закрытия, если кто-нибудь нажал на один из них. На самом деле ваш вопрос не был закрыт из-за того, что он не касался программирования или был слишком простым, а потому, что код содержит ошибки, о которых говорила проверка оболочки, о которой тег bash, который вы использовали, говорит вам использовать перед публикацией вопроса о bash. о вас (и поэтому многим людям, читающим ваш код, не придется обращаться).
@udippel: Одна проблема с вашим вопросом заключается в том, что вы задали два вопроса (почему это не удается? Как мне лучше написать?), и здесь вы видите, почему: пользователь Wiimm дал вам лучшее объяснение, почему это нужно делать, но без объяснений. почему ваш код не работает. Я дал вам объяснение, почему это не удается, но альтернативного решения нет. Теперь, если вы хотите принять ответ, какой из двух вы могли бы принять? Это основная причина, по которой у вас должен быть только один вопрос — это позволит вам выбрать лучший ответ.
Кроме того, ваша пометка странная. Например, вы отметили это как double, хотя в вашем вопросе нет ничего, связанного с типом C или C++ double
. Кроме того, я не вижу особой значимости тега выполнения, поскольку у вас нет проблем с фактическим запуском/выполнением процесса.
Моя рекомендация: работайте с массивами:
#!/usr/bin/env bash
cmd=(ls -l)
trio=( "${cmd[@]}" "${1}_DD_FF" )
"${trio[@]}"
Заменив echo
в вашем скрипте на xxd <<< ${trio}
, мы увидим:
00000000: 6c73 202d 6c20 2274 776f 2074 6872 6565 ls -l "two three
00000010: 5f44 445f 4646 220a _DD_FF".
Таким образом, ваша переменная trio
содержит строку ls -l "two three_DD_FF"
.
Когда вы запускаете ${trio}
, который можно было бы записать проще как $trio
, bash применяет к строке разделение слов, что дает следующие слова:
0: ls
1: -l
2: "two
3: three_DD_FF"
Следовательно, ls
вызывается с параметром опции (-l
) и двумя дополнительными аргументами, "two
и three_DD_FF"
.
Это отражено в сообщении об ошибке о том, что "two
и three_DD_FF"
не найдены.
Хороший ясный ответ. Но почему у вас есть обратные кавычки на первых двух пунктах вашего списка 0–4? Должно быть просто: 0: ls
потом 1: -l
потом 2: "two
потом 3: three_DD_FF"
.
@Raven: Понятия не имею, почему они туда попали. Я удалил их. Спасибо, что рассказали мне.
Ваш
ls
может быть какой-нибудь странной проверкой псевдонима, вот такtype ls