Вопрос или проблема
Я хочу «загрузить» файл с локальной системы на FTP-сервер, используя только sftp из bash-скрипта.
Я хочу подавить все сообщения, кроме ошибок:
sftp $user@$server 2>&1 1>/dev/null <<EOF
put $local_file_path $remote_file_path
EOF
Вывод команды при успешном выполнении:
Подключение к <server>...
Почему я все еще получаю это?
Вероятно, потому что Подключение к <server>...
отправляется в дескриптор файла 2 (stderr). Помните, что единственное различие между дескрипторами файлов 1 (stdout) и 2 (stderr) заключается в том, что первый традиционно используется для обычных сообщений, в то время как второй традиционно используется для отладочных или ошибочных выводов. Тем не менее, программа решает, что отправлять по одному каналу или другому; вы можете перенаправить один вывод или другой, но не можете решить, что будет выведено куда. Вы не можете заставить, например, так, чтобы только ошибки отправлялись в stderr.
У меня была такая же проблема, поэтому я придумал это решение. Это не 100% нормальный stderr
вывод, но это значительно помогает определить, что вызвало ошибку.
sftp $user@$server > /dev/null 2>&1 <<EOF
put $local_file_path $remote_file_path
EOF
sftp_status=$?
case $sftp_status in
0) sftp_error="";;
1) sftp_error="Ошибка 1: Общая ошибка - Неопределенная ошибка при копировании файла";;
2) sftp_error="Ошибка 2: Ошибка подключения к удаленному хосту";;
3) sftp_error="Ошибка 3: Назначение не является директорией, но должно быть";;
4) sftp_error="Ошибка 4: Не удалось подключиться к хосту";;
5) sftp_error="Ошибка 5: Соединение потеряно по какой-то причине";;
6) sftp_error="Ошибка 6: Файл не существует";;
7) sftp_error="Ошибка 7: Нет разрешения на доступ к файлу";;
8) sftp_error="Ошибка 8: Неопределенная ошибка от sshfilexfer";;
9) sftp_error="Ошибка 9: Несоответствие протокола передачи файла";;
65) sftp_error="Ошибка 65: Хосту не разрешено подключаться";;
66) sftp_error="Ошибка 66: Ошибка протокола";;
67) sftp_error="Ошибка 67: Неудалось обменяться ключами";;
68) sftp_error="Ошибка 68: Не удалось аутентифицировать хост";;
69) sftp_error="Ошибка 69: Ошибка MAC";;
70) sftp_error="Ошибка 70: Ошибка сжатия (не используется в SSH2)";;
71) sftp_error="Ошибка 71: Сервис недоступен";;
72) sftp_error="Ошибка 72: Протокол версии не поддерживается";;
73) sftp_error="Ошибка 73: Ключ хоста не подлежит проверке";;
74) sftp_error="Ошибка 74: Соединение потеряно";;
75) sftp_error="Ошибка 75: Отключено приложением";;
76) sftp_error="Ошибка 76: Слишком много подключений";;
77) sftp_error="Ошибка 77: Отменено пользователем";;
78) sftp_error="Ошибка 78: Больше нет доступных методов аутентификации";;
79) sftp_error="Ошибка 79: Недопустимое имя пользователя";;
255) sftp_error="Ошибка 255: Произошла ошибка в SSH";;
*) sftp_error="Неизвестная ошибка sftp";;
esac
echo $sftp_error
Коды статуса основаны на этом списке: https://support2.microfocus.com/techdocs/2487.html
Какие коды статуса вы получаете, зависит от вашего клиента sftp
Некоторые клиенты используют только коды статуса 0, 1 и 255
Причина, почему я все еще получаю это?
Потому что sftp
выводит это сообщение в stderr
, и вы перенаправили stderr
в stdout
, то есть в ваш терминал, с помощью 2>&1
.
Вы видите, что дескрипторы файлов, такие как &1
, не являются ссылками (или указателями, если вы знакомы с этой концепцией) — это значения. Поэтому, когда вы вводите 2>&1
, что на самом деле происходит:
- Ваш шелл определяет, куда указывает дескриптор файла 1 (
stdout
) в данный момент. В вашем случае вы выполняете это в интерактивном шелле, поэтомуstdout
указывает на ваш терминал. - Дескриптор файла 2 (
stderr
) также устанавливается на ваш терминал.
Таким образом, когда вы позже изменяете первый дескриптор файла (с помощью 1>/dev/null
), это не отражается на перенаправлении stderr
, потому что stderr
не зависит от stdout
ни в каком смысле.
Чтобы перенаправить все в /dev/null
, вам следует учитывать, как значения дескрипторов файлов копируются друг из друга; это приведет вас к реорганизации перенаправлений следующим образом:
sftp $user@$server 1>/dev/null 2>&1
Теперь ваш stdout
установлен в /dev/null
, а затем stderr
установлен на текущее значение stdout
, то есть /dev/null
. Ура!
Жаль, что никто не ответил на это правильно.
Попробуйте:
sftp_command 2>&1 >/dev/null | grep -v 'Подключение к'
Который перенаправляет stderr в stdout, а затем перенаправляет stdout в /dev/null, оставляя вам отображать все, кроме Подключение к.
Я просто привык добавлять “1>/dev/null 2>/dev/null” в конце строк, так как иногда 1>2 или 2>1 работает на одной системе, такой как Linux, но не работает на другой, такой как SCO/HPUX, но перенаправление 1 и 2 отдельно, кажется, работает на всех системах каждый раз…
Ответ или решение
Когда вы используете команду sftp
для передачи файла с вашего локального компьютера на FTP-сервер, важно правильно управлять выводом сообщений, чтобы оставлять только ошибки. Подробно рассмотрим, как это сделать.
Проблема
Ваша начальная команда:
sftp $user@$server 2>&1 1>/dev/null <<EOF
put $local_file_path $remote_file_path
EOF
В данной команде вывод stderr
перенаправляется в stdout
, а затем stdout
перенаправляется в /dev/null
. Однако, поскольку перенаправление stderr
происходит первичным, оно всё равно направляется в терминал, и вы продолжаете видеть сообщение о подключении к серверу, которое отправляется в stderr
.
Правильный подход к перенаправлению вывода
Для корректного подавления всех сообщений, кроме ошибок, вы можете воспользоваться следующим способом.
sftp $user@$server >/dev/null 2>&1 <<EOF
put $local_file_path $remote_file_path
EOF
Однако данное решение также не устраивает вашу задачу полностью, поскольку оно полностью подавляет вывод всех сообщений, включая ошибки. Лучше использовать более целенаправленное подавление сообщений.
Использование grep
для фильтрации выводов
Если вы хотите спрятать конкретные сообщения, например, "Connecting to", вы можете использовать grep
следующим образом:
sftp $user@$server 2>&1 | grep -v 'Connecting to' | grep -v 'sftp> '
Это перенаправит stderr
в stdout
и затем уберет строки, содержащие "Connecting to". Таким образом, вы получите вывод ошибок без дополнительных сообщений о соединении.
Обработка статусов команды
Чтобы обработать результат выполнения команды sftp
, вы можете воспользоваться переменной $?
, которая содержит код завершения последней выполненной команды. Пример:
sftp $user@$server >/dev/null 2>&1 <<EOF
put $local_file_path $remote_file_path
EOF
sftp_status=$?
case $sftp_status in
0) echo "Успешно";;
1) echo "Ошибка 1: Общая ошибка - не определенная ошибка в копировании файла";;
2) echo "Ошибка 2: Не удалось подключиться к удаленному хосту";;
# Добавьте сюда другие коды ошибок по вашему выбору
*) echo "Неизвестная ошибка SFTP";;
esac
Заключение
Таким образом, для успешной настройки вывода сообщений в SFTP вам необходимо использовать правильные перенаправления и фильтры. При помощи команды grep
вы сможете оставить только важные сообщения, а также использовать статусные коды для обработки результатов выполнения. Используйте предложенные методы, чтобы добиться оптимального управления выводом сообщений в вашем bash-скрипте.
Если у вас возникнут дополнительные вопросы или потребуется помощь, не стесняйтесь обращаться!