Итак, я пытаюсь содержимое большого блочного устройства, используя split следующим образом:

split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> /root/myfilecopy.gz"' /dev/myblockdev

Но когда я сохраняю имя удаленного файла в переменной окружения и использую эту переменную в split filter , он не работает:

remote_file=/root/myfilecopy.gz
split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> $remote_file"' /dev/myblockdev

Вот что я получаю:

bash: -c: line 0: syntax error near unexpected token `newline'
bash: -c: line 0: `gzip >> '
split: with FILE=x00, exit 1 from command: cat | ssh root@192.168.0.105 "gzip >> $remote_file"

Похоже, переменная окружения неправильно развернута внутри команды фильтра.

Любая подсказка, как это исправить?

Благодарю.

1 ответ1

1

Усложненный?

Переменные в стороне на мгновение. Это ваш основной код:

 split --bytes 10M --numeric-suffixes --filter='cat | ssh root@$remote_ip "gzip >> /root/myfilecopy.gz"' /dev/myblockdev

Прежде всего: cat здесь бесполезна.

Далее я думаю, что добавление результатов последовательных gzip -s в один и тот же файл (>>) отменяет работу split . Теоретически, в конце вы получите gzip /dev/myblockdev , как если бы вы это сделали:

ssh root@$remote_ip 'gzip > /root/myfilecopy.gz' < /dev/myblockdev

На практике я бы подумал о состоянии гонки. Я ожидаю, что split будет запускать ssh -s последовательно на локальной стороне; но я не удивлюсь, если при некоторых обстоятельствах различные буферы или лаги на удаленной стороне станут причиной записи следующего gzip до того, как закончится предыдущий. Это повредит myfilecopy.gz . Открытие файла только один раз предотвращает это.

Если бы вы использовали $FILE (смотрите man 1 split) и писали в несколько файлов, я бы увидел смысл в использовании split . Обратите внимание, что это не приведет к состоянию гонки, поскольку каждый из этих файлов будет открыт только один раз.

Вывод: split , вероятно, бесполезен, cat наверняка бесполезна; неосторожное добавление может повредить полученный файл.


Вопрос, о котором вы спрашивали

Хорошо, давайте предположим, что у вас есть причины использовать split и >> таким образом (хотя ваш cat все еще бесполезен).

Ваша текущая оболочка знает значение $remote_file , но split и ее фильтры являются дочерними процессами, и они не наследуют переменную, если вы не export ее заранее. Несуществующая переменная расширяется до нуля, поэтому соответствующий фрагмент выглядит как gzip >> (newline) , отсюда и ошибка.

То же самое относится и к $remote_ip . Я предполагаю, что в вашем коде вы используете не $remote_ip а фактический IP-адрес 192.168.0.105 . Отныне я использую remote_ip в качестве заполнителя (то есть не переменной оболочки) для фактического IP-адреса.

Для export:

remote_file="/root/myfilecopy.gz"
export remote_file
# now your split command should utilize the variable as you expected

В качестве альтернативы вы можете сделать вашу текущую оболочку развернутой $remote_file в момент запуска split . Первоначально строка $remote_file осталась без изменений из-за одинарных кавычек. Следующий синтаксис изменяет тип кавычек только для этой одной переменной:

split --bytes 10M --numeric-suffixes --filter='ssh root@remote_ip "gzip >> '"$remote_file"'"' /dev/myblockdev
#                                                     close a single quote ^              ^ and open again
#                                             variable inside double quotes ^^^^^^^^^^^^^^

Таким образом, split никогда не получает литерал $remote_file , он получает его значение.


Еще одна проблема

Если бы вы имели remote_file="/root/myfile copy.gz" , пространство в пути разделило бы его на два аргумента на удаленной стороне. По этой причине более надежный подход требует дополнительного цитирования. Это вышеупомянутый подход "без экспорта" с дополнительными кавычками (экранируется с \):

split --bytes 10M --numeric-suffixes --filter='ssh root@remote_ip "gzip >> \"'"$remote_file"'\""' /dev/myblockdev

Давайте раздеть это шаг за шагом. Среди других аргументов, split будет выглядеть следующим образом:

--filter=ssh root@remote_ip "gzip >> \"/root/myfile copy.gz\""

Это запустит этот фильтр в bash:

ssh root@remote_ip "gzip >> \"/root/myfile copy.gz\""

Тогда ssh увидит это как один из аргументов:

gzip >> "/root/myfile copy.gz"

Таким образом, на удаленной стороне перенаправление будет "/root/myfile copy.gz" . Без этих добавленных цитат вы бы получили:

gzip >> /root/myfile copy.gz

что эквивалентно

gzip copy.gz >> /root/myfile

Дополнительные примечания

  • Если локальный процессор достаточно быстрый, рассмотрите gzip перед ssh . Таким образом вы проталкиваете меньше данных через сетевую ссылку (и); особенно если вы подготовили свое блочное устройство к хорошему сжатию .
  • Если вы намеревались запустить несколько процессов gzip чтобы получить преимущество от нескольких ядер ЦП, то обратите внимание, что они все равно будут работать последовательно; если нет - состояние гонки - вы этого не хотите. Познакомьтесь с pigz .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .