Почему на вид один и тот же файл не исполняется в linux?

У меня есть два практически одинаковых скрипта test.sh и updated_pack.sh на Линуксе. Оба содержат строку: sudo apt-get upgrade -y >> /var/log/update_script.log.

Скрипт test.sh весит 54 байта (согласно ls -l) и нормально запускается. Скрипт update_script.sh же, в свою очередь, весит 55 байтов и не запускается, выдавая ошибку:

Permission deniedin 1: /var/log/updated_script.log

Оба файла имеют одинаковые права, в том числе и право на запуск.

Произведя hexdump, я обнаружил, что оба файла практически одинаковы, за исключением двух последних байтов. Что это за символы в конце?

Скриншот:

введите сюда описание изображения


Ответы (1 шт):

Автор решения: Anton Menshov

Дело в символах новой строки. И, скорее всего, копировании и вставке из Windows в Linux.


Если файл test.sh создан "печатаньем символов внутри какого-либо текстового редактора" в Linux (без копирования\вставки) и состоит строго из:

sudo apt-get upgrade -y >> /var/log/update_script.log

то мы имеем

> hexdump test.sh 
0000000 7573 6f64 6120 7470 672d 7465 7520 6770
0000010 6172 6564 2d20 2079 3e3e 2f20 6176 2f72
0000020 6f6c 2f67 7075 6164 6574 735f 7263 7069
0000030 2e74 6f6c 0a67 

что соответствует файлу, который у вас успешно запускается.

Теперь немного магии. Берем вышеупомянутый файлик test.sh и меняем в нем имеющийся Unix-овский символ LF (Line Feed) новой строки \n на CR (Carriage Return) \r:

> cat test.sh | tr -s '\n' '\r' > update_pack.sh
> hexdump update_pack.sh 
0000000 7573 6f64 6120 7470 672d 7465 7520 6770
0000010 6172 6564 2d20 2079 3e3e 2f20 6176 2f72
0000020 6f6c 2f67 7075 6164 6574 735f 7263 7069
0000030 2e74 6f6c 0d67 

мы видим, что воследний код с 0a67 поменялся на 0d67, что собственно и есть у вас на скриншоте.

Теперь разберемся с тем, что такое 000a. Хм... так а это юникодовский символ для LF (Line Feed).

Теперь мы начинаем понимать, что примерно произошло: последовательность CRLF используется для новых строк в Windows. Значит в какой-то момент произошло копирование из одного источника в другой в несопоставимой форме, да еще с юникодом, и все это попало на вентилятор. К счастью, вентилятор стандартизован и просто не запустился.

Скрипты чувствительны к таким вещам и строки должны заканчиваться LF. Обоснование:

POSIX 3.206:

3.206 Line

A sequence of zero or more non- characters plus a terminating character.

POSIX 3.243

3.243 Newline Character ()

A character that in the output stream indicates that printing should start at the beginning of the next line. It is the character designated by '\n' in the C language. It is unspecified whether this character is the exact sequence transmitted to an output device by the system to accomplish the movement to the next line.

Решение: сделать так, чтобы в скриптах для линуксового шелла символ новой строки был правильным: \n.

Еще полезно прочитать:

→ Ссылка