Как я могу назначить вывод команды для переменной оболочки?

76

Я хочу присвоить результат выражения переменной и объединить его со строкой, а затем повторить его. Вот что у меня есть:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

Но это выводит:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

Итак, похоже, что это не присваивается $thefile, а печатается по мере выполнения.

Натан Г.
источник

Ответы:

108

Назначение оболочки - это одно слово без пробела после знака равенства. Итак, то, что вы написали, присваивает пустое значение thefile; кроме того, поскольку назначение группируется с помощью команды, оно делает thefileпеременную среды, и назначение является локальным для этой конкретной команды, то есть только вызов lsвидит назначенное значение.

Вы хотите захватить вывод команды, поэтому вам нужно использовать подстановку команд :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(Некоторые литературные источники показывают альтернативный синтаксис thefile=`ls …`; синтаксис обратных кавычек эквивалентен синтаксису долларовых скобок, за исключением того, что цитирование внутри обратных кавычек иногда бывает странным, поэтому просто используйте $(…).)

Другие замечания о вашем сценарии:

  • Объединять -t(сортировать по времени) с -U(не сортировать) не имеет смысла; просто используйте -t.
  • Вместо того, чтобы использовать grepдля сопоставления скриншотов, яснее передать подстановочный знак lsи использовать headдля захвата первого файла:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • Обычно плохая идея разбирать выводls . Это может плохо работать, если у вас есть имена файлов с непечатными символами. Однако сортировка файлов по дате затруднительна ls, поэтому это приемлемое решение, если вы знаете, что в именах файлов не будет непечатаемых символов или обратной косой черты.

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

    echo "Most recent screenshot is: $thefile"

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

  • Вам не нужны точки с запятой в конце строки. Они избыточны, но безвредны.
  • В сценарий оболочки это часто хорошая идея для включения set -e. Это говорит оболочке о выходе, если какая-либо команда завершается неудачно (возвращая ненулевой статус).

Если у вас есть GNU find (в частности, если вы используете не встроенный Linux или Cygwin), есть другой подход к поиску самого последнего файла: findсписок файлов и их даты, а также использование sortи tailизвлечение самого младшего файла.

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

Если вы хотите написать этот скрипт на zsh вместо bash, есть гораздо более простой способ перехватить новейший файл, потому что zsh имеет квалификаторы glob, которые позволяют использовать подстановочные знаки не только для имен, но и для метаданных файла. (om[1])Часть после того, как шаблон отборочные GLOB; omсортирует совпадения по возрасту (т. е. по времени изменения, сначала [1]самое новое) и извлекает только первое совпадение. Все совпадения должны быть в скобках, потому что технически это массив, поскольку глобализация возвращает список файлов, даже если это [1]означает, что в данном конкретном случае список содержит (максимум) один файл.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"
Жиль "ТАК - перестань быть злым"
источник
7
Вау! Больше информации, чем я мог бы надеяться. Большое спасибо за ваш ответ; Я действительно ценю все усилия, которые вы вложили в это! Вы только что показали мне, что мне действительно есть чему поучиться. Я собираюсь купить книгу о Bash: P.
Натан Дж
3
Это то, что я бы назвал окончательным ответом! :)
Alex
4

Если вы хотите сделать это с помощью нескольких строк / нескольких команд / с, то вы можете сделать это:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

Или же:

output=$(
#multiline/multiple command/s
)

Пример:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Выход:

first
second
third
Jahid
источник
Это хороший ответ. Есть ли способ, которым я могу указать переменную как часть команды? напримерoutput=$(echo $someVariable)
Зак Смит