Почему rc.local не выполняет все мои команды, и что я могу с этим поделать?

У меня есть следующее rc.local скрипт:

#!/bin/sh -e## rc.local## This script is executed at the end of each multiuser runlevel.# Make sure that the script will "exit 0" on success or any other# value on error.## In order to enable or disable this script just change the execution# bits.## By default this script does nothing.sh /home/incero/startup_script.sh/bin/chmod +x /script.shsh /script.shexit 0

Первая строка, startup_script.sh на самом деле загружает script.sh файл для /script.sh упоминается в третьей строке.

К сожалению, похоже, что он не делает скрипт исполняемым или не запускает скрипт. Запускает rc.local файл вручную после запуска работает отлично. Может ли chmod не запускаться при запуске или что-то в этом роде?

Вы можете пропустить весь путь до Быстрое Решение но это не обязательно лучший вариант. Поэтому я рекомендую сначала прочитать все это.

rc.local не допускает ошибок.

rc.local не предоставляет способа разумного восстановления после ошибок. Если какая-либо команда завершается неудачей, она прекращает выполнение. Первая строка, #!/bin/sh -e, заставляет его выполняться в оболочке , вызываемой с помощью -e флаг. То -e флаг - это то, что создает скрипт (в данном случае, rc.local) прекратите выполнение при первом сбое команды в нем.

Ты хочешь rc.local вести себя подобным образом. Если команда завершается неудачей, вы выполняете нет хотите, чтобы он продолжал работать с любыми другими командами запуска, которые могут полагаться на его успех.

Поэтому, если какая-либо команда завершится неудачей, последующие команды выполняться не будут. Проблема здесь в том, что /script.sh не запустился (не то чтобы он потерпел неудачу, см. Ниже), так что, скорее всего, какая-то команда перед сбоем завершилась неудачей. Но какой из них?

Было ли это /bin/chmod +x /script.sh?

Нет.

chmod работает нормально в любое время. При условии, что файловая система, содержащая /bin был смонтирован, вы можете запустить /bin/chmod. И /bin монтируется перед rc.local бежит.

При запуске от имени root, /bin/chmod редко терпит неудачу. Он завершится неудачей, если файл, с которым он работает, доступен только для чтения, и вероятно сбой, если файловая система, в которой он находится, не поддерживает разрешения. Ни то, ни другое здесь маловероятно.

Между прочим, sh -e это только причина, по которой это действительно было бы проблемой, если chmod провалившийся. Когда вы запускаете файл сценария, явно вызывая его интерпретатор, не имеет значения, помечен ли файл как исполняемый. Только если в нем говорилось /script.sh будет ли иметь значение исполняемый бит файла. Поскольку в нем говорится sh /script.sh, это не так (если , конечно , /script.sh называет себя пока он выполняется, что может привести к сбою из-за того, что он не является исполняемым, но маловероятно, что он вызывает сам себя).

Так что же потерпело неудачу?

sh /home/incero/startup_script.sh провалившийся. Почти наверняка.

Мы знаем, что он запустился, потому что он загрузился /script.sh.

(В противном случае было бы важно убедиться, что он действительно запущен, на случай, если каким-то образом /bin не было в путь--rc.local не обязательно имеет то же самое PATH как и у вас, когда вы вошли в систему. Если /bin не были в rc.localпути, для этого потребуется sh будет выполняться как /bin/sh. С тех пор, как он действительно запустился, /bin в PATH, что означает , что вы можете запускать другие команды , расположенные в /bin, не уточняя полностью их имена. Например, вы можете запустить просто chmod скорее, чем /bin/chmod. Однако, в соответствии с вашим стилем в rc.local, Я использовал полные имена для всех команд, кроме sh, всякий раз, когда я предлагаю вам запустить их.)

Мы можем быть совершенно уверены /bin/chmod +x /script.sh никогда не бегал (иначе вы бы увидели, что /script.sh был казнен). И мы знаем sh /script.sh тоже не был запущен.

Но он загрузился /script.sh. Это удалось! Как он мог потерпеть неудачу?

Два значения успеха

Есть две разные вещи, которые человек может иметь в виду, когда он / она говорит, что команда выполнена успешно:

  1. Он сделал то, что вы хотели, чтобы он сделал.
  2. Он сообщил, что это удалось.

И так же обстоит дело с неудачей. Когда человек говорит, что команда не удалась, это может означать:

  1. Он сделал не то, что вы хотели, чтобы он сделал.
  2. Он сообщил, что это не удалось.

Сценарий, выполняемый с sh -e, как rc.local, прекратит выполнение при первом выполнении команды сообщает, что это не удалось. Не имеет значения, что на самом деле сделала команда.

Если только вы не намерены для startup_script.sh чтобы сообщить о сбое, когда он делает то, что вы хотите, это ошибка в startup_script.sh.

  • Некоторые ошибки мешают скрипту делать то, что вы хотите, чтобы он делал. Они влияют на то, что программисты называют его побочные эффекты.
  • И некоторые ошибки не позволяют скрипту правильно сообщать о том, удалось ли ему это сделать или нет. Они влияют на то, что программисты называют его возвращаемое значение (что в данном случае является статус выхода).

Наиболее вероятно, что startup_script.sh сделал все, что следовало, кроме сообщил, что это не удалось.

Как Сообщается Об Успехе Или Неудаче

Сценарий - это список из нуля или более команд. Каждая команда имеет статус выхода. Предполагая, что при фактическом запуске скрипта нет сбоев (например, если интерпретатор не смог прочитать следующую строку скрипта во время его выполнения), статус завершения скрипта равен:

  • 0 (успех), если сценарий был пустым (т.Е. не содержал команд).
  • N, если сценарий завершился в результате выполнения команды exit N, где N это какой-то код выхода.
  • Код выхода последней команды, выполненной в скрипте, в противном случае.

Когда исполняемый файл запускается, он сообщает свой собственный код выхода - они предназначены не только для сценариев. (И технически, коды выхода из сценариев - это коды выхода, возвращаемые оболочками, которые их запускают.)

Например, если программа на языке Си заканчивается exit(0);, или return 0; в его main() функция, код 0 передается операционной системе, которая предоставляет его вызывающему процессу (который может, например, быть оболочкой, из которой была запущена программа).

0 означает, что программа выполнена успешно. Любое другое число означает, что он потерпел неудачу. (Таким образом, разные цифры иногда могут указывать на разные причины сбоя программы.)

Команды, предназначенные для сбоя

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

Что-то подобное, вероятно, происходит в startup_script.sh, как раз перед тем, как он перестанет работать. То последняя команда для выполнения в скрипте, вероятно, сообщается об ошибке (хотя его "сбой" может быть совершенно нормальным или даже необходимым), что заставляет скрипт сообщать о сбое.

Тесты, обреченные на провал

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

Например, предположим, я забыл, равно ли 4 5. К счастью, я знаю сценарии оболочки:

if [ 4 -eq 5 ]; then    echo "Yeah, they're totally the same."fi

Здесь тест [ -eq 5 ] терпит неудачу, потому что в конце концов получается 4 × 5. Это не значит, что тест не был выполнен правильно; так оно и было. Его задача состояла в том, чтобы проверить, если 4 = 5, а затем сообщить об успехе, если это так, и о неудаче, если нет.

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

Даже несмотря на то, что echo оператор никогда не выполняется, if блок в целом действительно возвращает успех.

Однако предположил, что я написал его короче:

[ 4 -eq 5 ] && echo "Yeah, they're totally the same."

Это обычная стенография. && это логический и оператор. A && выражение, которое состоит из && с утверждениями с обеих сторон возвращает false (сбой), если обе стороны не возвращают true (успех). Просто как обычный и.

Если кто-то спросит вас: "Дерек ходил в торговый центр и думал о бабочке?" и вы знаете, что Дерек не ходил в торговый центр, вам не нужно утруждать себя выяснением, думал ли он о бабочке.

Аналогично, если команда слева от && сбой (false), весь && выражение немедленно завершается ошибкой (false). Заявление на правой стороне && никогда не запускается.

Здесь, [ 4 -eq 5 ] бежит. Это "сбой" (возвращает false). Так что весь && выражение терпит неудачу. echo "Yeah, they're totally the same." никогда не убегает. Все вело себя так, как и должно быть, но эта команда сообщает неудача (даже несмотря на то, что в остальном эквивалентно if условное обозначение выше сообщает об успехе).

Если бы это был последний оператор в сценарии (и сценарий добрался до него, а не завершился в какой-то момент перед ним), весь сценарий сообщил бы неудача.

Есть много тестов, помимо этого. Например, существуют тесты с || ("или"). Однако приведенного выше примера должно быть достаточно, чтобы объяснить, что такое тесты, и дать вам возможность эффективно использовать документацию для определения того, является ли конкретный оператор / команда тестом.

sh против. sh -e, Пересмотренный

С то #! линия (см. также этот вопрос) в верхней части /etc/rc.local имеет sh -e, операционная система запускает сценарий так , как если бы он был вызван с помощью команды:

sh -e /etc/rc.local

В отличие от этого, ваши другие сценарии, такие как startup_script.sh, беги без то -e флаг:

sh /home/incero/startup_script.sh

Следовательно, они продолжают работать, даже когда команда в них сообщает об ошибке.

Это нормально и хорошо. rc.local должен быть вызван с помощью sh -e и большинство других скриптов, включая большинство скриптов, выполняемых rc.local-- не должен.

Просто не забудьте запомнить разницу:

  • Сценарии выполняются с sh -e завершите отчет об ошибке в первый раз, когда содержащаяся в них команда завершает отчет об ошибке.

    Это как если бы сценарий был одной длинной командой, состоящей из всех команд в скрипте, соединенных с && операторы.

  • Сценарии выполняются с sh (без -e) продолжают выполняться до тех пор, пока они не дойдут до команды, которая завершает (завершает) их, или до самого конца скрипта. Успех или неудача каждой команды по существу не имеет значения (если только следующая команда не проверяет ее). Сценарий завершается со статусом завершения последнего запуска команды.

Помочь Вашему Сценарию Понять, Что В Конце Концов Это Не Такой Уж Провал

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

Вы смотрите на то, что происходит непосредственно перед тем, как он закончит работать.

  1. Если команда завершилась неудачей, когда она должна была завершиться успешно, выясните причину и устраните проблему.

  2. Если команда завершилась неудачей, и это было правильно, то предотвратите распространение этого состояния сбоя.

    • Один из способов предотвратить распространение статуса сбоя - это выполнить другую команду, которая завершится успешно. /bin/true не имеет побочных эффектов и сообщает об успехе (так же, как /bin/false ничего не делает и тоже терпит неудачу).

    • Другой способ - убедиться, что сценарий завершается с помощью exit 0.

      Это не обязательно то же самое, что exit 0 находясь в конце сценария. Например, может быть if-блок, в котором скрипт завершается внутри.

Лучше всего знать, что заставляет ваш скрипт сообщать о сбое, прежде чем заставлять его сообщать об успехе. Если он действительно каким-то образом терпит неудачу (в смысле не делает то, что вы хотите, чтобы он делал), вы действительно не хотите, чтобы он сообщал об успехе.

Быстрое Решение проблемы

Если вы не можете сделать startup_script.sh завершите отчет об успешном завершении, вы можете изменить команду в rc.local который запускает его, так что команда сообщает об успехе, даже если startup_script.sh этого не произошло.

В настоящее время у вас есть:

sh /home/incero/startup_script.sh

Эта команда имеет те же побочные эффекты (т.е. побочный эффект запуска startup_script.sh), но всегда сообщает об успехе:

sh /home/incero/startup_script.sh || /bin/true

Помните, лучше знать, почему startup_script.sh сообщает о сбое и исправляет его.

Как работает быстрое решение

На самом деле это пример || тест, ан или тест.

Предположим, вы спросите, вынес ли я мусор или почистил сову. Если я вынес мусор, я могу честно сказать "да", даже если я не помню, чистил я сову или нет.

Команда слева от || бежит. Если это удастся (true), то правая сторона не должна запускаться. Так что, если startup_script.sh сообщает об успехе, то true команда никогда не выполняется.

Однако, если startup_script.sh сообщает о сбое [Я не вынес мусор], то результат /bin/true [если бы я почистил сову] вопросы.

/bin/true всегда возвращает успех (или истинный, как мы иногда это называем). Следовательно, вся команда выполняется успешно, и следующая команда в rc.local может бежать.

Дополнительное примечание об успехе/неудаче, истине/ложи, нуле/ненулевом значении.

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

Это вызывает большое замешательство у разработчиков сценариев оболочки, изучающих такие языки программирования, как C, и у программистов на C, изучающих сценарии оболочки, чтобы обнаружить:

  • В сценариях оболочки:

    1. Возвращаемое значение, равное 0 означает успех и/или истинный.
    2. Возвращаемое значение чего-то другого, кроме 0 означает неудача и/или ложный.
  • В программировании на языке Си:

    1. Возвращаемое значение, равное 0 означает ложный..
    2. Возвращаемое значение чего-то другого, кроме 0 означает истинный.
    3. Не существует простого правила для определения того, что означает успех, а что означает неудачу. Иногда 0 означает успех, в других случаях это означает неудачу, в других случаях это означает, что сумма двух чисел, которые вы только что добавили, равна нулю. Возвращаемые значения в общем программировании используются для передачи широкого спектра различных видов информации.
    4. Числовой статус завершения программы должен указывать на успех или неудачу в соответствии с правилами для сценариев оболочки. То есть, даже несмотря на 0 означает false в вашей программе C, вы все равно заставляете свою программу возвращать 0 в качестве кода выхода, если вы хотите, чтобы он сообщил об успешном завершении (которое затем интерпретируется оболочкой как истинный).

Вы можете (в вариантах Unix, которые я использую) переопределить поведение по умолчанию, изменив:

#!/bin/sh -e

К:

#!/bin/sh

В начале скрипта. Флаг "-e" указывает sh на выход при первой ошибке. Длинное имя этого флага - "errexit", поэтому исходная строка эквивалентна:

#!/bin/sh --errexit

@Bryce 's ответ это совершенно нормально в определенных ситуациях - в зависимости от того, что вы используете rc.local для. Если, конечно, абсолютно необходимо, чтобы после первой ошибки ничего не произошло -e это именно то, чего вы хотите. Но rc.local может использоваться для многих целей, и у вас могут быть там вещи, которые не зависят от успеха других вещей.

Однако большая часть причин, по которым я отвечаю, заключается в том, чтобы объяснить, что вы можете сделать, чтобы выяснить, где ваш rc.local пошел не так. Я настоятельно рекомендую иметь что-то подобное прямо в начале вашего сценария rc.local:

exec 2> /tmp/rc.local.log      # send stderr from rc.local to a log fileexec 1>&2                      # send stdout to the same log fileset -x                         # tell sh to display commands before executiondate                           # good practice to record when stuff happened

измените первую строку с:#!/bin/sh -eк!/bin/sh -e