Я знаю, что переменные в 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

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

2 ответа2

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"
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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .