Вывод команды tail приводит к неожиданному выводу?

8

Эта команда, если она запускается одна, дает ожидаемый результат (последняя строка crontab):

tail -n 1 /etc/crontab

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

sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

Почему эта команда создала дополнительные данные?

Davidw
источник
3
Что здесь echoдля тебя делает? Рассмотрим такжеtail -n 1 /etc/crontab | sudo tee /path/to/file >/dev/null
Ctrl-Alt-Delor
1
Что не так с echo $(stuff)?
Камиль Мачоровский

Ответы:

22

В строке crontab есть одна или несколько звездочек *, указывающих «в любое время». Когда эта строка подставляется из подстановки команд, результат выглядит примерно так:

echo * * * * * cmd > /path/to/file

В то время как большинство дальнейшие разложения не применяются к выходу подстановки команд, расширение путь является (как это поле разложения) :

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

Расширение пути - это то, что превращается *.txtв список совпадающих имен файлов (глобализация), где *совпадает все. Конечным результатом является то, что вы получаете каждое (не скрытое) имя файла в рабочем каталоге, указанное для каждого *в строке crontab.


Вы можете исправить это, заключив в кавычки расширение, если код, который вы разместили, был представителем более сложной команды:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

но проще говоря просто потерять echoполностью

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

Это должно делать то, что вы хотите, и это также проще (единственное другое отличие материала в том, что в этой версии пропущено разделение полей, которое в противном случае произошло бы, поэтому пробелы не будут свернуты).

Майкл Гомер
источник
5
Поскольку / etc / crontab почти всегда доступен для чтения всем, все хитрости "bash -c" на самом деле не нужны: tail -n -1 /etc/crontab | sudo tee /path/to/fileэто, как я обнаружил, наименее подверженный ошибкам способ перенаправления вывода в файлы, требующие привилегий суперпользователя.
Бас
5

Давайте рассмотрим каталог с этими файлами:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

Теперь давайте запустим команду tail:

$ tail -n 1 crontab
f*

Выше - последняя строка, crontabи это то, что мы ожидаем. Однако:

$ echo $(tail -n 1 crontab)
file1 file2 file3

Двойные кавычки устраняют эту проблему:

$ echo "$(tail -n 1 crontab)"
f*

Без двойных кавычек результат подстановки команды раскрывается оболочкой. Одним из расширений является расширение пути . В приведенном выше случае это означает, что f*он расширяется, чтобы соответствовать каждому имени файла, начинающемуся с f.

Если вы явно не хотите расширения оболочки, поместите все переменные оболочки и / или подстановки команд в двойные кавычки.

John1024
источник
4

Механизм globing shell расширится *до локального файла.

Строка crontab, скорее всего, будет *заполнителем для любого.

например, эта строка в crontab работает в воскресенье в 7.47, первая звезда означает любой день, вторая - любой месяц.

47  7 * * 0 /run/on/sunday

тогда вы tailи выпускаете

echo 47  7 * * 0 /run/on/sunday

это расширится *до локального файла.

Archemar
источник