#shell #unix #foreach #tcl
#оболочка #unix #foreach #tcl
Вопрос:
Я получаю пустые значения для переменной $ tclItem в приведенном ниже скрипте в цикле foreach, даже если список содержит значения.
Не могли бы вы, пожалуйста, проверить и посмотреть, чего мне не хватает?
FTP_USER="xxxxx"
FTP_SERVER="xxxxx"
FTP_PWD="xxxx"
FTP_DROP_DIR="DROP/Archive"
LOGFILE="tmplog.txt"
FILES_TO_ARCHIVE="$(cat $LOGFILE | grep '.txt' | awk ' !/Fetching/' | tr -d 'r') "
echo "Files to Archive..."
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
echo $FILES_TO_ARCHIVE
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
expect <<END
spawn sftp $FTP_USER@$FTP_SERVER
expect "*password: "
send "$FTP_PWDr";
expect "sftp> "
foreach tclItem {$FILES_TO_ARCHIVE } {
#puts $tclItem
send "ls $FTP_DROP_DIR/$tclItemr"
expect "sftp> "
};
send "quitr"
END
и вот результат, который я получаю.
Files to Archive.....
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
test1.txt test2.txt test3.txt test4.txt
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
spawn sftp abcdef@sftp.server.com
abcdef@sftp's password:
sftp> ls DROP/Archive/
sftp> ls DROP/Archive/
sftp> ls DROP/Archive/
sftp> ls DROP/Archive/
sftp> invalid command name "test2.txt"
while executing
"test2.txt"
Комментарии:
1. В общем, хранить списки имен файлов в формате, ориентированном на строку, является врожденной ошибкой, поскольку именам файлов в UNIX разрешено содержать новые строки (и пробелы, и кавычки, и другое синтаксически чувствительное содержимое) самостоятельно.
2. Рассматривали ли вы возможность использования SFTP-клиента, построенного с поддержкой сценариев, например
lftp
?sftp
Инструментарий библиотеки Python paramiko аналогично будет лучшим инструментом для работы, чем expect sftp.3.Чтобы было ясно, почему это ведет себя так, как оно есть — расширения переменных в heredoc происходят один раз, когда генерируется heredoc, то есть перед
expect
вызовом. Таким образом, ко времениforeach tclItem
запускаexpect
,$tclItem
интерпретатор оболочки уже заменил на пустое значение во время оценки heredoc.4. В любом случае, использование heredoc для генерации кода со значениями, которые не были просмотрены человеком, всегда является плохой идеей. Кто-то отправляет файл с именем, которое выполняет произвольную команду при интерпретации expect, и следующее, что вы знаете, это то, что вы запускаете код по своему выбору — загружаете руткит, открываете обратную оболочку и т.д.
5. перед циклом foreach добавьте
puts [list $FILES_TO_ARCHIVE]
— каков результат?
Ответ №1:
Вместо использования подстановки строк для ввода данных в код (практика, сопряженная с проблемами безопасности), передавайте свои переменные из bash
в expect
через среду.
# Environment variables should be lowercase except for special/reserved names meaningful
# to the operating system; http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
ftp_user="xxxxx"
ftp_server="xxxxx"
ftp_pwd="xxxx"
ftp_drop_dir="DROP/Archive"
logfile="/tmp/log.txt"
files_to_archive="$(awk '/[.]txt/ amp;amp; !/Fetching/' <"$logfile" | tr -d 'r')"
export ftp_user ftp_server ftp_pwd ftp_drop_dir files_to_archive
expect <<'END'
spawn sftp $env(ftp_user)@$env(ftp_server)
expect "*password: "
send "$env(ftp_pwd)r";
expect "sftp> "
set fileList [split $env(files_to_archive) "n"]
foreach tclItem $fileList {
send "ls $env(ftp_drop_dir)/$tclItemr"
expect "sftp> "
};
send "quitr"
END
Примечание:
- Использование
export
приводит к тому, что переменные оболочки становятся доступными в качестве переменных окружения в подпроцессах. В expect$env(foo)
позволяет прочитать переменную средыfoo
. - Использование
<<'EOF'
вместо<<EOF
приводит к тому, что bash передает код вexpect
точно таком виде, как он есть, без изменений. (Таким образом,$tclItem
остается$tclItem
вместо замены пустой строкой еще доexpect
начала).
Комментарии:
1. Даже переменные среды несовершенны; файлы и каналы являются лучшими методами, как с точки зрения безопасности, так и с точки зрения обработки необычных символов. (Это абсолютно единственный безопасный способ перемещения двоичных данных.) Многие языки хороши для чтения и записи файлов…
2. @DonalFellows, я был бы признателен за разъяснение ваших аргументов против использования переменных окружения. Конечно, они были доступны для чтения другими пользователями в некоторых старых операционных системах, но я не знаю ни одной ОС, которая обычно считается хорошо поддерживаемой и достаточно безопасной, где это имеет место сегодня.
3. @DonalFellows, … другая вещь, которая кажется мне разумным возражением, заключается в том, что переменные среды не могут содержать NUL, поэтому их нельзя использовать для представления всех возможных списков имен файлов без шагов кодирования или экранирования — но OP извлекает их имена из файла, разделенного новой строкой, поэтому имена по определению не могут содержать литералы новой строки, что делает текущее разделение на основе новой строки адекватным набору возможных входных данных здесь. Этот код должен корректно обрабатывать имена со всеми возможными символами, отличными от перевода строки.