чувствительны ли скрипты оболочки к кодировке и окончаниям строк

bash shell sh


Я делаю приложение NW.js на Mac и хочу запустить его в режиме dev,дважды щелкнув по значку.Первый шаг,я пытаюсь заставить мой скрипт оболочки работать.

Используя VSCode в Windows (я хотел выиграть время), я создал файл run-nw в корне моего проекта, содержащий следующее:

#!/bin/bash

cd "src"
npm install

cd ..
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &

но я получаю этот результат:

$ sh ./run-nw

: command not found  
: No such file or directory  
: command not found  
: No such file or directory  

Usage: npm <command>

where <command> is one of:  (snip commands list)

(snip npm help)

npm@3.10.3 /usr/local/lib/node_modules/npm  
: command not found  
: No such file or directory  
: command not found

Я правда не понимаю:

  • кажется, что он принимает пустые строки в качестве команд. В моем редакторе (VSCode) я пытался заменить \r\n на \n (если \r создает проблемы), но это ничего не меняет.
  • кажется, что он не находит папки (с инструкцией dirname или без нее), или, может быть, он не знает о команде cd ?
  • кажется, что он не понимает аргумент install в npm
  • Что меня действительно удивляет, так это то, что приложение все еще работает (если я установил npm install вручную) ...

Не сумев заставить его работать должным образом,и подозреваю что-то странное с самим файлом,я создал новый прямо на Mac,используя vim на этот раз.Я ввел точно такие же инструкции,и...теперь он работает без проблем.
Разница в двух файлах показывает ровно нулевую разницу.

Какая разница? Что может заставить не работать первый сценарий? Как я могу это узнать?

Update

Следуя рекомендациям принятого ответа, после того, как вернулись неправильные окончания строк, я проверил несколько вещей. Оказывается, поскольку я скопировал ~/.gitconfig со своего компьютера с Windows, у меня было autocrlf=true , поэтому каждый раз , когда я изменял файл bash в Windows, он сбрасывал окончание строк на \r\n .
Итак,в дополнение к запуску dos2unix (который вам придётся установить с помощью Homebrew на mac),если вы используете Git,проверьте вашу конфигурацию.




Answer 1 Anthony Geoghegan


Да. Bash скрипты являются чувствительными к линии окончаний, как в самом скрипте и в данных , которые он обрабатывает. Они должны иметь конец строки в стиле Unix, то есть каждая строка заканчивается символом перевода строки (десятичное число 10, шестнадцатеричное 0A в ASCII).

DOS/Windows окончания строк в скрипте

В Windows или DOS-стиле окончания строк заканчиваются символом возврата каретки,за которым следует символ подачи строки.Если файл скрипта был сохранен с концами строк в стиле Windows,Bash видит файл как

#!/bin/bash^M
^M
cd "src"^M
npm install^M
^M
cd ..^M
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M

Примечание: я использовал символ каретки для представления непечатаемых символов, т. ^M используется для представления символов возврата каретки (представленных как \r в других контекстах); это та же самая техника, которую использовали cat -v и Vim.

В этом случае возврат каретки ( ^M или \r ) не рассматривается как пробел. Bash интерпретирует первую строку после шебанга (состоящего из одного символа возврата каретки) как имя команды / программы для запуска.

  • Поскольку нет команды с именем ^M , она печатает : command not found
  • Поскольку нет каталога с именем "src"^M (или src^M ), он печатает : No such file or directory
  • Он передает install^M вместо install в качестве аргумента npm , что заставляет npm жаловаться.

Окончания строк DOS/Windows во входных данных

Как и выше,если у вас есть входной файл с возвратом каретки:

hello^M
world^M

тогда это будет выглядеть совершенно нормально в редакторах и при записи на экран, но инструменты могут давать странные результаты. Например, grep не сможет найти строки, которые явно находятся там:

$ grep 'hello$' file.txt || grep -x "hello" file.txt
(no match because the line actually ends in ^M)

Вложенный текст вместо этого перезапишет строку,так как возвращаемая каретка перемещает курсор к началу строки:

$ sed -e 's/$/!/' file.txt
!ello
!orld

Сравнение строк покажется неудачным,несмотря на то,что при записи на экран строки выглядят одинаково:

$ a="hello"; read b < file.txt
$ if [[ "$a" = "$b" ]]
  then echo "Variables are equal."
  else echo "Sorry, $a is not equal to $b"
  fi

Sorry, hello is not equal to hello

Solutions

Решение заключается в преобразовании файла для использования строковых окончаний в стиле Unix.Есть несколько способов сделать это:

  1. Это можно сделать с dos2unix программы dos2unix :

    dos2unix filename
  2. Откройте файл в способным текстовом редакторе (Sublime, Notepad ++, а не Блокнот) и настроить его для сохранения файлов с символами конца строки Unix, например, с помощью Vim, выполните следующую команду до (ре) экономия:

    :set fileformat=unix
  3. Если у вас есть версия утилиты sed , которая поддерживает опцию -i или --in-place , например, GNU sed , вы можете запустить следующую команду, чтобы удалить завершающие возвраты каретки:

    sed -i 's/\r$//' filename

    В других версиях sed вы можете использовать перенаправление вывода для записи в новый файл. Обязательно используйте другое имя файла для цели перенаправления (его можно переименовать позже).

    sed 's/\r$//' filename > filename.unix
  4. Точно так же фильтр перевода tr может использоваться для удаления нежелательных символов из его входных данных:

    tr -d '\r' <filename >filename.unix

Сигвин Бэш

С портом Bash для Cygwin есть настраиваемая опция igncr , которую можно настроить так, чтобы игнорировать возврат каретки в конце строки (предположительно, потому что многие из ее пользователей используют собственные программы Windows для редактирования своих текстовых файлов). Это можно включить для текущей оболочки, запустив set -o igncr .

Установка этой опции применима только к текущему процессу оболочки, поэтому она может быть полезна при поиске файлов с посторонними возвратами каретки. Если вы регулярно сталкиваетесь со сценариями оболочки с окончанием строки DOS и хотите, чтобы этот параметр был установлен постоянно, вы можете установить переменную окружения SHELLOPTS (все заглавные буквы) для включения igncr . Эта переменная окружения используется Bash для установки параметров оболочки при запуске (перед чтением любых файлов запуска).

Полезные коммунальные услуги

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

  • Концы строк Unix: Bourne-Again shell script, ASCII text executable
  • Концы строк в Mac: Bourne-Again shell script, ASCII text executable, with CR line terminators
  • Окончание строк DOS: Bourne-Again shell script, ASCII text executable, with CRLF line terminators

Версия утилиты cat для GNU имеет опцию -v, --show-nonprinting которая отображает непечатаемые символы.

dos2unix утилита специально написана для преобразования текстовых файлов между Unix, Mac и окончаниями строк DOS.

Полезные ссылки

В Википедии есть отличная статья, охватывающая множество различных способов пометить конец строки текста, историю таких кодировок и то, как обрабатываются переводы строк в разных операционных системах, языках программирования и интернет-протоколах (например, FTP).

Файлы с классическими линейными окончаниями Mac OS

В Classic Mac OS (до OS X) каждая строка заканчивалась символом возврата каретки (десятичное 13, шестнадцатеричный 0D в ASCII). Если файл скрипта был сохранен с такими окончаниями строк, Bash увидит только одну длинную строку, например:

#!/bin/bash^M^Mcd "src"^Mnpm install^M^Mcd ..^M./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &^M

Поскольку эта единственная длинная строка начинается с восьмиугольника ( # ), Bash рассматривает строку (и весь файл) как один комментарий.

Примечание. В 2001 году Apple выпустила Mac OS X, основанную на операционной системе NeXTSTEP, основанной на BSD . В результате OS X также использует конец строки только для LF в стиле Unix, и с тех пор текстовые файлы, оканчивающиеся символом CR, стали чрезвычайно редкими. Тем не менее, я думаю, что стоит показать, как Bash будет пытаться интерпретировать такие файлы.




Answer 2 CONvid19


На JetBrains продукции (PyCharm, PhpStorm, IDEA и т.д.), вы должны будете click на CRLF / LF , чтобы переключаться между этими двумя типами разделители строк ( \r\n и \n ).

enter image description here enter image description here




Answer 3 Igor Soudakevitch


Еще один способ избавиться от нежелательного символа CR ('\ r') - запустить команду tr , например:

$ tr -d '\r' < dosScript.py > nixScript.py



Answer 4 tripleee


Исходя из дубликата, если проблема в том, что у вас есть файлы, имена которых содержат ^M в конце, вы можете переименовать их с помощью

for f in *$'\r'; do
    mv "$f" "${f%$'\r'}"
done

Вы должным образом хотите исправить то, что привело к тому, что эти файлы вначале имели неправильные имена (вероятно, сценарий, который их создал, должен быть dos2unix , а затем перезапущен?), Но иногда это неосуществимо.

$'\r' Синтаксис Bash-специфический; если у вас другая оболочка, возможно, вам нужно использовать другие обозначения. Возможно, посмотрите также разницу между sh и bash




Answer 5 danR


Самый простой способ на MAC/Linux-создать файл с помощью команды 'touch',открыть этот файл с помощью VI или VIM редактора,вставить свой код и сохранить.Это автоматически удалит символы windows.