2015-11-11 2 views
0

Я довольно новичок в скрипте bash и, вероятно, не буду этого делать часто. У меня есть следующий скрипт для проверки последних обновлений в определенном месте, но я получаю: location: Undefined variable, и метка времени не извлекается успешно.неопределенная переменная с использованием переменной оболочки в команде ssh

#!/bin/bash 

servers=('x' 'y') 
results=() 
location="/path/to/location/" 

for server in "${servers[@]}"; do 
    output=$(ssh "$server" 'find "${location}" -exec stat \{} --printf="%y\n" \; | sort -n -r | head -n 1') 
    x=(${server}:${output}) 
    results+=(${x}) 
done 

echo "Printing results..." 
for res in "${results[@]}"; do 
    echo "$res" 
done 

Моя цель состоит в том, чтобы напечатать в конце:

x:<timestamp of last update> 
y:<timestamp of last update> 

мне интересно, что это неправильно.

+1

'/ bin/sh' не поддерживает массивы, кстати, поэтому' servers = ('x' 'y') 'не может работать, не меняя вашу оболочку на bash, ksh или подобное. –

+0

То же самое для 'x = (...)'. –

+0

, который сказал, что ваше 'location' также не определено, потому что оно находится во внешних одинарных кавычках, поэтому, конечно, оно не будет расширяться. Лучший способ исправить это требует еще большего расширения bash, несовместимого с вашим # #/bin/sh' shebang. –

ответ

0

Непосредственная проблема заключается в том, что 'foo "$bar"' все еще имеет расширение bar внутри одиночных кавычек (что делает двойные кавычки буквальными, а не синтаксическими). Это не так - буквальные цитаты, когда они переданы в ssh, затем могут быть выполнены оболочкой на удаленной стороне ssh-соединения, но она имеет побочный эффект, который не расширяется $location; таким образом, ссылка на "${location}" делает ее удаленной оболочке, где она не определена.


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

printf -v cmd1 '%q ' find "$location" -exec stat --printf='%y\n' '{}' + 
ssh "$server" "$cmd1 | sort -n -r | head -n 1" 

Трубопровод компоненты не входят в cmd1, потому что - бежать содержание следует рассматривать в качестве данных, а не синтаксис - printf %q приведет буквенные символы в | передаются как аргументы find, а не как директивы оболочки. (Это именно то, что вам нужно : это связано с любым литеральным содержимым внутри $location, которое в противном случае могло бы использоваться для запуска как команды!).

Таким образом, сама оболочка (с помощью магии printf %q) будет отвечать за расширение переменных и правильно спасаясь вашу команду find таким образом, чтобы гарантировать, что содержание рассматривается в качестве данных (то есть. Поэтому location из '/tmp/$(rm -rf /)' не удаляет файлы).

+0

Спасибо, что сработал ..Другой вопрос: я получаю только дату, тогда как если я запускаю cmd непосредственно в терминале, я получаю и время. –

+0

Действительно ли это работает даже непосредственно в терминале * на удаленной системе *? В любом случае, если вы хотите, чтобы переносные временные метки были отформатированы таким образом, который не является специфическим для параметров языковой системы системы, я бы предложил изменить вашу строку формата, чтобы перенести их как время эпохи и преобразовать ее в правильную строку даты/времени на местном уровне. –

+0

BTW, если у вас есть stat с '--printf', это довольно четкий индикатор, который вы нацеливаете на систему с помощью GNU-утилит, поэтому вы должны иметь' find' с '-exec {} +' тоже. –

Смежные вопросы