пустое значение в каждом цикле — TCL — unix скрипт

#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 извлекает их имена из файла, разделенного новой строкой, поэтому имена по определению не могут содержать литералы новой строки, что делает текущее разделение на основе новой строки адекватным набору возможных входных данных здесь. Этот код должен корректно обрабатывать имена со всеми возможными символами, отличными от перевода строки.