СофтХабрахабр

[Перевод] Осторожнее с редактированием bash-скриптов

Предположим, я написал такой bash-скрипт с названием delay.sh. Как думаете, что он делает?

#!/bin/bashsleep 30#rm -rf --no-preserve-root /echo "Time's up!"

Похоже, он ожидает 30 секунд, а затем выводит сообщение на экран. Здесь никаких фокусов — он делает именно это. Там есть опасная команда в середине, но она закомментирована и не выполняется.

Представьте, что я снова запускаю этот скрипт, но теперь мне не хочется ждать 30 секунд — это слишком долго. Я открываю вторую консоль, меняю sleep 30 на sleep 3, затем сохраняю файл. Как думаете, что будет теперь?

Ну, через 30 секунд скрипт удалит все мои файлы.
Так происходит потому, что bash считывает содержимое скрипта фрагментами по мере выполнения, отслеживая смещение в байтах. Когда я удаляю один символ из строки sleep, смещение для начала следующей команды указывает на r в #rm вместо #. С точки зрения интерпретатора, # смещается на предыдущую строку, поэтому он выполняет команду начиная с rm.

Это можно подтвердить, наблюдая за системными вызовами bash в Linux. Вот выдача strace bash delay.sh, с комментариями и в сокращении.

# Открытие скриптаopenat(AT_FDCWD, "delay.sh", O_RDONLY) = 3 # Парсинг первой строчки (до 80 символов)read(3, "#!/bin/bash\nsleep 30\n#echo \"Don'"..., 80) = 64 # Возврат к началуlseek(3, 0, SEEK_SET) = 0 # Переключение на на файловый дескриптор 255dup2(3, 255) = 255 # Чтение 64-байтового куска файла, чтобы получить командуread(255, "#!/bin/bash\nsleep 30\n#echo \"Don'"..., 64) = 64 # Поместить курсор обратно в конец команды, которую мы собираемся выполнить# Offset 21 is the `#`lseek(255, -43, SEEK_CUR) = 21 # Приостановка выполнения, уход в sleepwait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 2072 # До возвращения wait4 файл редактируется с `30` на `3` # Чтение 64-байтового куска файла, чтобы получить следующую команду# В этом демо я заменил опасную команду на echoread(255, "echo \"Don't execute me\"\necho \"Ti"..., 64) = 42 # Bash решает выполнить оба echo одновременно без нового чтения# Очевидно, что-то идёт не такwrite(1, "Don't execute me\n", 17) = 17write(1, "Time's up!\n", 11) = 11 # Чтение следующего фрагмента и обнаружение конца файлаread(255, "", 64) = 0

Поэтому будьте осторожны при запуске редактировании bash-скрипта, который выполняется в данный момент. Он может выполнить неверную команду или сделать что-то очень неожиданное.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»