У меня есть файл file.txt с командой, сохраненной в одной строке (эта команда действительна при запуске в консоли), и я хочу выполнить ее в одной строке с помощью sh, например:
cat file.txt | eval
чего не хватает? есть какие-нибудь советы?
а что если у меня есть файл со многими командами (по одной на каждую строку) и я хочу выполнить только одну команду (одну целую строку)? Моя первая идея:
head -n5 | tail -n1 | eval
решение1
eval
не считывает командную строку из stdin.
eval "$(cat file.txt)"
# or easier, in ksh/bash/zsh
eval "$(<file.txt)"
# usually you cannot be sure that a command ends at the end of line 5
eval "$(head -n 5 file.txt)"
Вместо eval
можно использовать стандартный .
или bash
/ zsh
/ ksh
source
, если команды в любом случае находятся в файле:
source ./file
(обратите внимание, что важно добавить, что ./
. В противном случае source
ищет file
в , $PATH
прежде чем рассматривать file
в текущем каталоге. Если в режиме POSIX, bash
даже не будет рассматривать file
в текущем каталоге, даже если не найден в $PATH
).
Конечно, это не работает с выбором части файла. Это можно сделать так:
head -n 5 file.txt >commands.tmp
source ./commands.tmp
Или (с ksh93, zsh, bash):
source <(head -n 5 file.txt)
решение2
Итак... решение вашего вопроса на 100% возможно и (в контексте make
) важно.
Я столкнулся с этим при работе с make-файлом, и, учитывая сложность вложения команд bash в make-файл, который уже используется $(...)
для вызова переменных, было бы неплохо иметь возможность сделать именно то, о чем вы спрашиваете.
Вместо использования eval
просто используйте системную команду awk
или :perl
// command_list.sh:
echo "1"
echo "2"
echo "3"
// command line prompt:
$: cat command_list.sh | awk '{system($0)}'
1
2
3
И, командное здание:
// a readable version--rather than building up an awk miniprogram,
// split into logical blocks:
$: cat inputs.txt | awk '{print "building "$1" command "$2" here "}' | awk '{system($0)}'
решение3
Технически невозможно оценить что-либо в вашей текущей среде, если вы хотите прочитать это из конвейера, поскольку каждая команда в конвейере выполняется как отдельный процесс.
Однако, в зависимости от того, чего вы пытаетесь достичь, это может не иметь значения. В любом случае, вот запуск eval
против содержимого трубы:
❯ echo echo hi | eval "$(cat -)"
hi
Вот пример подвоха. Что-то вроде следующего не будет напечатано 1
:
❯ echo a=1 | eval "$(cat -)"; echo $a
Вместо этого нам придется сделать следующее:
❯ echo a=1 | { eval "$(cat -)"; echo $a; }
1
решение4
Если вы не полны решимости использовать eval
, используйте sh
:
cat file.txt | sh
Да, он будет работать в отдельном экземпляре sh, но этот факт документирован самостоятельно.
А также есть while read
:
cat file.txt | while read cmd; do eval $cmd; done
Чтобы выполнить команду в 5-й строке файла file.txt:
sed -n 5p file.txt | sh
(Я понимаю, что это старый вопрос.)