запутался в переменных в bash

0

Я знаю, что переменные в bash не имеют типа, но я не совсем понимаю, какое значение им присвоено.

Следующий простой скрипт отлично работает в bash

#!/bin/bash 
tail -n +2 /cygdrive/c/workdir\ \(newco\,\ LLC\)/workfile.txt > \
/cygdrive/c/workdir\ \(newco\,\ LLC\)/workfile2.txt

Тем не менее, следующее не

#!/bin/bash
tmpdir=/cygdrive/c/workdir\ \(newco\,\ LLC\)
tail -n +2 $tmpdir/workfile.txt > $tmpdir/workfile2.txt

Есть ли объяснение этому поведению?

с промежутками
источник

Ответы:

3

Вы должны обязательно указывать в кавычках переменные, которые могут содержать пробелы. В вашем случае, tailполучает три файла: /cygdrive/c/workdir, (newco,и LLC)потому , что $tmpdirесть три слова , разделенные пробелами.

Я знаю, что вы избежали пробела при назначении $tmpdir, но это просто служило для того, чтобы каждое слово не интерпретировалось как отдельная команда во время назначения. Если вы тогда echo $tmpdir, вы получите
/cygdrive/c/workdir (newco, LLC), и это то, что передается tail.

Чтобы избежать этого, цитата $tmpdir:

tail -n +2 "${tmpdir}/workfile.txt" > "${tmpdir}/workfile2.txt"
savanto
источник
Благодарю. В чем разница между "$ {tmpdir} /workfile.txt" и "$ tmpdir / workfile.txt"?
gappy
2
Фигурные скобки используются для обеспечения правильного анализа имен переменных. Если у вас есть переменная с именем, tmpвы можете сделать что-то вроде ${tmp}dir/workfile.txtзамены вместо целой $tmpdir(при условии, что она тоже существует).
Гроностай
0

Введите первую строку в bash, а затем отобразите эту переменную, вот что вы увидите:

$ tmpdir=/cygdrive/c/workdir\ \(newco\,\ LLC\)
$ echo $tmpdir
/cygdrive/c/workdir (newco, LLC)

Если еще не ясно, кто виноват, добавьте echoв начале второй строки и заключите в двойные кавычки. Вы увидите, какая команда на самом деле выполняется:

$ echo "tail -n +2 $tmpdir/workfile.txt > $tmpdir/workfile2.txt"
tail -n +2 /cygdrive/c/workdir (newco, LLC)/workfile.txt > /cygdrive/c/workdir (newco, LLC)/workfile2.txt

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

Как @savanto предложил в своем ответе, вы можете заключить переменные в двойные кавычки, чтобы лишние пробелы не обрабатывались как разделители аргументов. Эта строка:

tail -n +2 "${tmpdir}/workfile.txt" > "${tmpdir}/workfile2.txt"

будет выглядеть так после подстановки переменной:

tail -n +2 "/cygdrive/c/workdir (newco, LLC)/workfile.txt" > "/cygdrive/c/workdir (newco, LLC)/workfile2.txt"

Это наиболее распространенный и предпочтительный способ решения этой проблемы. Если вы отчаянно хотите избежать двойных кавычек, вы можете попробовать двойное экранирование: сначала при присваивании, затем после подстановки.

$ tmpdir=/cygdrive/c/workdir\\\ \\\(newco\\\,\\\ LLC\\\)
$ echo $tmpdir
/cygdrive/c/workdir\ \(newco\,\ LLC\)

Быстрое объяснение тройной обратной косой черты: обратная косая черта означает: «обрабатывать следующий символ как обычный символ, игнорировать любое специальное значение» (т.е. избегать его ). Первая обратная косая черта является escape-символом, поэтому вторая будет рассматриваться как обратная косая черта, а не как escape-символ. Таким образом, двойной обратный слеш производит один обратный слеш. Третий просто избегает следующего символа, такого как пробел или скобка. Например a\\\ bстанет a\ b.

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

tail -n +2 /cygdrive/c/workdir\ \(newco\,\ LLC\)/workfile.txt > /cygdrive/c/workdir\ \(newco\,\ LLC\)/workfile2.txt
gronostaj
источник