Расширение Bash скобки после косой черты

12

Я пытаюсь скопировать файл с другим именем в тот же каталог, используя расширение скобки. Я использую Bash 4.4.18.

Вот что я сделал:

cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}

но я получаю эту ошибку:

cp: cannot stat '/home/xyz/some/dir/{my-file-to-rename.bin,': No such file or directory

Даже простое расширение скобки, подобное этому, выдает мне ту же ошибку:

cp {my-file-to-rename.bin, new-name-of-file.bin}

Что я делаю неправильно?

nanangarsyad
источник

Ответы:

29

Фигурные скобки синтаксис принимает запятые, но он не принимает пробел после запятой. Во многих языках программирования пробелы после запятой являются обычным явлением, но не здесь. В Bash наличие пробела без кавычек не позволяет выполнить расширение скобок.

Уберите пробел, и он будет работать:

cp ~/some/dir/{my-file-to-rename.bin,new-name-of-file.bin}

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

cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin

Если вы хотите проверить эффект расширения скобок, вы можете использовать echoили printf '%s ', или printfс любой строкой формата, которую вы предпочитаете, чтобы сделать это. (Лично я просто использую echoдля этого, когда нахожусь в Bash, потому что echoвстроенная функция Bash по умолчанию не расширяет escape-последовательности и, таким образом, достаточно хорошо подходит для проверки того, какая команда на самом деле будет выполняться.) Например:

ek@Io:~$ echo cp ~/some/dir/{my-file-to-rename,new-name-of-file}.bin
cp /home/ek/some/dir/my-file-to-rename.bin /home/ek/some/dir/new-name-of-file.bin
Элия ​​Каган
источник
23

string{foo, bar}не расширение скобки; это два слова string{foo,и bar}. Чтобы использовать расширение скобок, скобки должны быть в одном слове. Вам придется либо удалить лишний пробел, если он вам не нужен, либо указать его, если он вам нужен:

$ printf "%s\n" aa{bb, cc}
aa{bb,
cc}
$ printf "%s\n" aa{bb,cc}
aabb
aacc
$ printf "%s\n" aa{bb," cc"}
aabb
aa cc
ilkkachu
источник
+1 за наглядный пример и спасибо. Я дам свой голос, когда моей репутации достаточно. :)
nanangarsyad
Есть еще кое-что, что следует упомянуть о «словах оболочки». , .ак как оболочка знает где разделять слова;)
Сергей Колодяжный
-1

Баш относится к этому пространству так же, как к любому другому. Как IFS, внутренний разделитель полей. Это используется для разделения слов после раскрытия и разделения строк на слова с помощью встроенной команды read.

Оболочка обрабатывает каждый символ IFS как разделитель и разбивает результаты других расширений на слова на этих символах. Если IFS не установлен или его значение в точности равно значению по умолчанию, то последовательности, и в начале и в конце результатов предыдущих расширений игнорируются, и любая последовательность символов IFS, отсутствующая в начале или конце, служит для разделения слова. Если IFS имеет значение, отличное от значения по умолчанию, то последовательности пробельных символов пробела и табуляции игнорируются в начале и конце слова, пока символ пробела находится в значении IFS (символ пробела IFS). Любой символ в IFS, который не является пробелом IFS, наряду с любыми соседними символами пробела IFS ограничивает поле. Последовательность пробельных символов IFS также рассматривается как разделитель. Если значение IFS равно нулю,
-bash (1)

Вставив разделитель unescaped, вы сказали bash, что ваша команда и аргументы:

  1. «Ф»
  2. "~ / Некоторые / реж / {мой-файл к rename.bin"
  3. "Имя нового из-file.bin}"

Если бы у вас были кавычки или побег "\", вы бы получили:

  1. «Ф»
  2. "~ / some / dir / {my-file-to-rename.bin, \ new-name-of-file.bin}"

Что также не будет тем, что вы хотели, если только «new-name-of-file.bin» не является новым именем файла, которое вы хотели. Пространство включено. Поскольку сначала происходит расширение скобок, а затем расширение тильды, bash выполнит:

  1. «Ф»
  2. "/Path/to/home/some/dir/my-file-to-rename.bin"
  3. "/ path / to / home / some / dir / \ new-name-of-file.bin"

Простое удаление пробела исправит все это.

CDE
источник
5
В основном это хорошо, но вы, кажется, говорите, что разделение слов происходит cp ~/some/dir/{my-file-to-rename.bin, new-name-of-file.bin}и IFSвлияет на результат. Ни то, ни другое. Здесь пространство является метасимволом в токенизации ( шаг 2 ). См. 3.5.7 о том, когда происходит разделение. Попробуй IFS=xтогда printf '[%s]\n' {a,b} printf '[%s]\n' {a, b} printf '[%s]\n' {a,xb} printf '[%s]\n' {a, xb}.
Элия ​​Каган