× К оглавлению На главную Об авторе

Дата и время публикации:    

1. Назначение

Документ описывает использование статического анализатора кода shellcheck для скриптов сценария BASH, чтобы определить какие ошибки синтаксиса были допущены. Считаю очень полезным не только для улучшения качества кода, но и оптимизации в целях повышения быстродействия скрипта.

Использование shellcheck привожу на примере, разрабатываемого мной проекта "VHD enviroment", позволяющего запускать виртуальные диски на внешних или внутренних системах хранения данных, включая на разделы файловой системы fuseblk(ntfs, extfat и т.п.). В результате проверки скриптов были выявлены следующие ошибки:

Код ошибки Описание ошибки Рекомендация
SC1068 Недопускается наличие символов вокруг опрератора присвоения значения переменной (=) Удалить все символы вокруг оператора присвоения
SC1078 Значение строковой переменной не было закрыто второй двойной кавычкой закрыть двойной кавычкой
SC1087 Использование скобок при расширения массива Если ошибка произошла в регулярном выражении привести декларацию к требуемой
SC1090 Невозможно проследовать по указанному пути для source. Указать директиву # shellcheck source=/dev/null
SC1115 Наличие пробелов между # и ! в шебанге удалить пробелы
SC2001 Не рекомендуется использовать sed для замены Рекомендуется взамен использовать ${variable//search/replace}
SC2004 Ненужное использование $/${} на арифметических операциях Не использовать $/${} в арифметическом выражении $((..))
SC2012 Рекомендуется использовать find или указать полный путь ls с помощью команды $(which ls) получить полный путь
SC2025 Наложение букв вводимой команды на ранее введенную в командной строке при использовании ESC-последовательности
SC2034 Переменная не используется Проверить фактический код, а в случае использования в других скриптах использовать export
SC2046 Заключить в кавычки для превращения дробления слов см. рекомендацию к SC2086
SC2050 Использование имени переменной в виде константы перед именем переменной поставить $
SC2053 Значение переменной может быть скомкан, когда стоит справа от оператор сравнение =, == или != Обрамить кавычками
SC2070 -n не работает с не обрамленными кавычками (может сопровождаться SC2086) заключить в двойные кавычки или использовать ```[[ ]]` ``
SC2086 Значение переменной не окаймлено кавычками Рекомендуется окаймлять двойными кавычками строковое значение во избежание подстановки или разбиения
SC2116 Бессмысленное использование команды echo Взамен i'cmd $(echo foo)' следует использовать 'cmd foo'
SC2115 Предотвращение несанкционированного удаления от корня файловой системы (rootfs) при использовании команды rm использовать :? в rm -rf "${STEAMROOT:?}/"*, чтобы предотвратить удаление от корня rootfs командой rm, когда STEAMROOT не будет присвоено значение
SC2120 Отсутствует передача аргументов функции Передать "@"
SC2140 Нельзя применять многократно двойные ковычки в форме "A"B"C" Следует использовать форму записи "A\"B\"C"
SC2141 Отсутствует буквальный символ 'n' в присваиваемом значении Использовать специальный $..., алфавитно-буквенный символ или printf
SC2143 Повысить эффективность за счет сокращения количества итераций поиска с помощью grep до первого совпадения Использовать ! grep -q взамен сравнения вывода с [ -z .. ]
SC2148 Не указана целевая оболочка Указать шебанг #!/bin/bash в первой строке скрипта
SC2152 Возвращаемое значение функции должно быть в диапазоне от 0,...,255 Указать числовое значение от 0 до 255
SC2154 Ссылка не существующую на переменную, которая не была назначена Удалить или исправить имя переменной к существующей
SC2155 Требуется разделить декларацию и присвоение значения во избежание маскирования возвращаемого значения
SC2162 Выключить функцию экранирования специальных символов (backslash function) команды read Использовать read -r -p
SC2164 Осутствие проверки результата выполнения команды cd Использовать 'cd ...
SC2181 Не рекомендуется проверять результат выполнения команды mycmd с использованием $? в условном выражении указывать непосредственно if mycmd ;
SC2209 Нельзя присвоить команду,например, сформированную eval, без кавычек или var=$(command) Применить кавычки или var=$(command)
SC2236 Не рекомендуется использовать оператор НЕ с -z Вместо "! -z" cледует использвоать "-n"

2. Использование

2.1 Запуск анализатора


	$...hdv-env$ shellcheck -s bash bin/vhdenv-scripts

Опция -s или --shell указывает какой тип оболочки используется.

Например, вывод результатов проверки файла bin/vhdbash-newinst-exec до внесения обновлений:


	In bin/vhdbash-newinst-exec line 7:
	echo "${vhd_bold}VHD enviroment${vhd_normal}: replaced bash instance to be used $VHDENV_BASHRCFILE file"  
	      ^---------^ SC2154: vhd_bold is referenced but not assigned.
	                               ^-----------^ SC2154: vhd_normal is referenced but not assigned.
	
	
	In bin/vhdbash-newinst-exec line 8:
	exec env -i HOME=$(pwd) TERM=$TERM /bin/bash --rcfile $VHDENV_BASHRCFILE
	                 ^----^ SC2046: Quote this to prevent word splitting.
	                             ^---^ SC2086: Double quote to prevent globbing and word splitting.
	                                                      ^----------------^ SC2086: Double quote to prevent globbing and word splitting.
	
	Did you mean: 
		exec env -i HOME=$(pwd) TERM="$TERM" /bin/bash --rcfile "$VHDENV_BASHRCFILE"
	
	For more information:
	  https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt.
        https://www.shellcheck.net/wiki/SC2154 -- vhd_bold is referenced but not as...
        https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

Каждый отчет содержит детальное описание всех допущенных ошибок, окрашенные в красный, желтый и зеленый, и предваряемые строчкой Did you mean: рекомендаций, которые можно использовать "копипаст". Соответственно, цвета сигнализируют насколько найденные ошибки являются критическим, где красный является критической, а зеленый -- предупреждение.

2.2 Примеры исправления ошибок

SC2053


    In vhdenv-plugins/aarch64--glibc-cross/rules/activate-rule line 110:
          [[ $(basename "$a") == $2 ]] && return 1
                                 ^-- SC2053: Quote the right-hand side of == in [[ ]] to prevent glob matching.

Значение переменной $2 может быть скомкан, когда стоит справа от оператор сравнение =, == или != . Поэтому переменная "$2" должна быть обрамлена кавычками .

Результат. Корректируемый код:


    - [[ $(basename "$a") == $2 ]] && return 1
    + [[ $(basename "$a") == "$2" ]] && return 1

SC1068

Пример сообщения :


    VHDPLUGIN_STATUS = "$1" 
                     ^-- SC1068: Don't put spaces around the = in assignments (or quote to make it literal).

Результат. Корректируемый код:


    -VHDPLUGIN_STATUS = "$1"
    +VHDPLUGIN_STATUS="$1" 

SC1078

Пример сообщения :

 
    In bin/activate-run line 73:
    if [ "$(basename $PWD)" != "vhd-env" ] ; then 
                                       ^-- SC1078: Did you forget to close this double quoted string?

Результат. Корректируемый код:

 
**Невозможно привести, т.к. вторая окаймляющая кавычка была в другой языковой кодировки (ru_RU.UTF-8). Поэтому при переходе в корень рабочей директории плагина , должна устанавливаться к POSIX, как рекомендуют редакторы LFS .

SC1090

Пример сообщения :


	In bin/activate-run line 83:
	      source "$VHDENV_LIBPATH"
	             ^---------------^ SC1090: Can't follow non-constant source. Use a directive to specify location.

В результате, корректирующий код:


	--- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 21:46:40.094683100 +0300
	+++ /path/to/repo/vhdenv.git/bin/activate-run	2025-01-31 21:52:42.352328417 +0300
	@@ -79,7 +79,8 @@
	       vhd_deactivate
	       exit 1
	    elif [ -f "$VHDENV_ROOTDIR/bin/vhd-controls" ] ; then
	-      echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename "$VHDENV_LIBPATH") plug-in library" 
	+      echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename "$VHDENV_LIBPATH") plug-in library"
	+      # shellcheck source=/dev/null 
	       source "$VHDENV_LIBPATH"
	       if vhdenv_reinit ; then   
	          vhdenv_deactivate

Для того, чтобы ShellCheck не обращал внимание на файл, указанный в $VHDENV_LIBPATH, указана директива # shellcheck source=/dev/null .

SC1115

Пример сообщения :


	In bin/vhdenv-install line 1:
	# !/bin/bash
	^-- SC1115: Remove spaces between # and ! in the shebang.

В результате, корректирующий код:


	diff --git a/vhdenv-install b/vhdenv-install
	index 4a1f593..cc85f6e 100644
	--- a/vhdenv-install
	+++ b/vhdenv-install
	@@ -1,4 +1,4 @@
	-#!/bin/bash
	+# !/bin/bash
	#
 	# The script provides to verify installed VHDEND binary 
	# if they are accorded to latest version of git repository

Между символом шебанга(#) и восклицательного знака был удален пробел.

SC2001

Пример сообщения:


	In bin/vhd-controls line 268:
	               mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')" 
	                            ^-- SC2001: See if you can use ${variable//search/replace} instead.

В результате, исправляющий код:


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 16:13:58.546079300 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 18:28:23.694968296 +0300
	@@ -265,7 +265,8 @@
	            mount_ops="$VHDMOUNT_OPTIONS"
	            if [[ $1 == mount ]] && [ "$2" == "--root-owner" ] ; then 
	                echo "${vhd_bold}VHD enviroment :${vhd_normal} apply the root permission to $mpoint_name"
	-               mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')" 
	+#              mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
	+               mount_ops=${VHDMOUNT_OPTIONS/,user/} 
	            fi
	            echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
	            if [[ $1 == mount  ]] ; then

В данном случае, замена используется для удаления опции монтирования user , когда осуществляется монтирование дисков VHD с правами суперпользователя:


    vhdmount --root-owner native-linux && $VHDMNT_DIRPATH/native-linux

    drwxr-xr-x 6 root root  4096 янв 19 20:27 .
    drwxr-xr-x 1 user user  4096 фев  1 16:17 ..
    drwx------ 2 root root 16384 янв  9 05:19 lost+found

и обычного пользователя:


    vhdmount --root-owner native-linux && $VHDMNT_DIRPATH/native-linux
    
    drwxr-xr-x 6 user user  4096 янв 19 20:27 .
    drwxr-xr-x 1 user user  4096 фев  1 16:17 ..
    drwx------ 2 root root 16384 янв  9 05:19 lost+found

Замена использования sed в данном случае разумна и позволяет снизить затраты за счет использования простой замены ${variable//search/replace} .

SC2004


	In bin/vhd-controls line 463:
	                      VHDFILE_BLOCKS=$(($VHDFILE_SIZE * (1024**2) / 4096 ))
	                                        ^-----------^ SC2004: $/${} is unnecessary on arithmetic variables.

В результате, корректирующий код:


    --- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-02 10:09:48.758470200 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-02 10:14:28.472463774 +0300
    @@ -459,7 +459,7 @@
                              _vhdenv_help   
                              return $retfail
                           fi
    -                      VHDFILE_BLOCKS=$(($VHDFILE_SIZE * (1024**2) / 4096 ))
    +                      VHDFILE_BLOCKS=$((VHDFILE_SIZE * (1024**2) / 4096 ))
                           echo "${vhd_bold}VHD enviroment:${vhd_normal} it is now, new file will be created"
                           echo "       VHD file name: $(basename "$VHDFILE_PATH")"  
                           echo "       VHD file size: $VHDFILE_SIZE MBytes"

Символ доллара ($) перед VHDFILE_SIZE был избыточен, т.к. производится арифиметическая операция.

SC2012

Пример сообщения:


	In bin/vhd-controls line 190:
	       vhdfns=$(ls $VHDFILE_DIRPATH/*.vhd | tr "\n" ",")
                    ^-----------------------^ SC2012: Use find instead of ls to better handle non-alphanumeric filenames.

В результате, исправляющий код:


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 13:44:39.935456600 +0300
    +++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 13:49:13.678094593 +0300
    @@ -187,7 +187,8 @@
        fi  
        VHDFILE_PATH_SAVE="$VHDFILE_PATH"
        export VHDFILE_PATH_SAVE 
    -   vhdfns=$($(which ls) "$VHDFILE_DIRPATH/*.vhd" | tr "\n" ",")
    +   vhdfns="$(find "$VHDFILE_DIRPATH" -type f -name "*.vhd" -print | tr "\n" ",")"
        printf  "%-16s : %-42s  %-32s  %-s\n" "DEVICE" "STAT" "VHD FILE" "MOUNT POINT"
        IFS=,
        for vhdfn in $vhdfns ;  

Вместо команда ls рекомендуется использовать find на ряду с tr .

SC2025

Пример сообщения:


	In bin/vhdbashrc line 6:
	PS1="\e[0;31m[\u@\h \W]\$ \e[m "
	    ^--------------------------^ SC2025: Make sure all escape sequences are enclosed in \[..\] to prevent line wrapping issues

Результат.Корректируемый код:

	
	--- /path/to/storage/vhd-env/bin/vhdbashrc	2025-02-01 10:21:17.700376100 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhdbashrc	2025-02-01 10:26:13.917239349 +0300
	@@ -3,7 +3,8 @@
         	 	 
	-PS1="\e[0;31m[\u@\h \W]\$ \e[m "
	+PS1="\[\e[0;31m[\u@\h \W]\$ \[\e[m\]"
	+
	 
	 set +h
	 umask 022

ESC-последовательность "\e[0;31m[\u@\h \W]$ \e[m " должна быть заключена в квадратные скобки, чтобы исключить проблем с нарушениями целостности строки. В данном случае, наблюдалось наложение букв вводимой команды на ранее введенную в командной строке:


    [user@home ~]$  vhdenv_deactivav_deactivate 

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

SC2027

Пример сообщения :


	In bin/activate-run line 90:
	         PS1="("$VHDENV_PROMPT_PREFIX")$PS1" 
	                ^-------------------^ SC2027: The surrounding quotes actually unquote this. Remove or escape them.

Результат. Корректируемый код:


	--- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 21:32:12.109692200 +0300
	+++ /path/to/repo/vhdenv.git/bin/activate-run	2025-01-31 21:46:32.258595628 +0300
	@@ -87,7 +87,7 @@
	          echo "${vhd_bold}VHD enviroment${vhd_normal}: activated."
	          VHDENV_PROMPT_PREFIX="vhdenv" 
	          _OLD_VHDENV_PS1="${PS1:-}"
	-         PS1="("$VHDENV_PROMPT_PREFIX")$PS1" 
	+         PS1="($VHDENV_PROMPT_PREFIX)$PS1" 
	          export THIS_SCRIPT VHDENV_PROMPT_PREFIX _OLD_VHDENV_PS1  PS1
	       fi
	    else

Для устранения этой ошибки было достаточно удалить лишние двойные кавычки в PS1="("$VHDENV_PROMPT_PREFIX")$PS1" .

SC2034

Пример сообщения:


    In bin/activate line 19:
    vhd_isready=true
    ^---------^ SC2034: vhd_isready appears unused. Verify use (or export if used externally).

Результат.Корректируемый код:


	--- /path/to/storage/vhd-env/bin/activate	2025-01-31 10:18:51.315017800 +0300
	+++ /path/to/vhdenv.git/bin/activate	2025-01-31 10:25:31.756845823 +0300
	@@ -16,7 +16,6 @@
	# So you need to use source command to the VHD enviroment to become active.	
	#
	 
	-vhd_isready=true
	vhd_bold=$(tput bold) 
 	vhd_normal=$(tput sgr0)

В данном случае, переменная vhd_isready явно не использовалась в исправляемом скрипте /path/to/storage/vhd-env/bin/activate и была удалена.

SC2046

Пример сообщения:


   In bin/activate-run line 84:
      elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
                                             ^----^ SC2046: Quote this to prevent word splitting.

Корректируемый код:


	@@ -81,7 +81,7 @@
           echo "Please run this script in 'bin/activate's directory." >&2
           vhd_deactivate
           exit 1
    -   elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
    +   elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f "$(pwd)"/bin/vhd-controls ] ; then
           export VHDENV_ROOTDIR=$(pwd)
           export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
           export VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)

shellcheck предупреждает об использовании read с опцией -r, потому что эта команда не будет включать функцию экранирования специальных символов (backslash function) :


    read -p "Enter textual sequence -> " n1 ; echo $n1
    Enter textual sequence -> aaaa \ bbbb
    aaaa bbbb

    read -r -p "Enter textual sequence -> " n1 ; echo $n1  
    Enter textual sequence -> aaaa \ bbbb 
    aaaa \ bbbb

SC2050


    In bin/vhd-controls line 321:
                                    if [[ $(type -t vhdplugin-activate) == function ]] && [ plugin_st != "X ." ] ; then
                                                                                                      ^-- SC2050: This expression is constant. Did you forget the $ on a variable?

Результат. Корректируемый код:


    -                                if [ plugin_st != "X ." ] ; then 
    +                                if [ "$plugin_st" != "X ." ] ; then 

Перед именем переменной была пропущен $ . В тоже время нужно помнить, если будет пропущен так же "plugin_st" != "X .", то такое проверочное условие будет всегда истинным.

SC2070

Пример сообщения:


    In bin/activate-run line 84:
    elif [ -n ${VHDENV_ROOTDIR:-} ] && [ -f $(pwd)/bin/vhd-controls ] ; then
             ^-----------------^ SC2070: -n doesn't work with unquoted arguments. Quote or use [[ ]].
             ^-----------------^ SC2086: Double quote to prevent globbing and word splitting.
                                           ^----^ SC2046: Quote this to prevent word splitting.

Корректируемый код:


    --- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 17:33:56.470564100 +0300
    +++ /path/to/vhdenv.git/bin/activate-run	2025-01-31 18:12:33.677659383 +0300
    @@ -81,7 +81,7 @@
        echo "Please run this script in 'bin/activate's directory." >&2
        vhd_deactivate
        exit 1
    -   elif [ -n ${VHDENV_ROOTDIR:-} ] && [ -f $(pwd)/bin/vhd-controls ] ; then
    +   elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
        export VHDENV_ROOTDIR=$(pwd)
        export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
        export  VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)

SC2086

Пример сообщения:


    In bin/activate-run line 71:
    if [ "$(basename $PWD)" != "vhd-env" ] ; then 
                 ^--^ SC2086: Double quote to prevent globbing and word splitting.

Корректируемый код:


	--- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 16:55:29.819356300 +0300
	+++ /path/to/vhdenv.git/bin/activate-run	2025-01-31 17:30:12.969198578 +0300
	@@ -68,7 +68,7 @@
	}
	 
	# checking on 'source' command usage and we are in vhd-env diroot
	-if [ "$(basename $PWD)" != "vhd-env" ] ; then 
	+if [ "$(basename "$PWD")" != "vhd-env" ] ; then   
	    echo "${vhd_bold}VHD enviroment error${vhd_normal}: you are out of the VHD enviroment."
	    vhd_isready=false   
	fi

Окаймлены кавычками переменные подставляемые значения переменных "$PWD" .

SC2116

Пример сообщения:


	In bin/vhd-controls line 265:
	           mount_ops=$(echo "$VHDMOUNT_OPTIONS")
	                     ^-------------------------^ SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.

В результате, исправляющий код :


    - mount_ops=$(echo "$VHDMOUNT_OPTIONS")
    +  mount_ops="$VHDMOUNT_OPTIONS"

Исключения для SC2116

Иногда, этот шаблон используется используется из-за побочного эффекта команды echo или расширения. Для примера, здесь $(echo ..) используется для расширения маски (glob):


	glob="*.png"
	files="$(echo $var)"

В таком виде echo не бесполезно, код является проблематичным, потому что он составлен из имен файлов, разделяемый пробелами, и это возможно будет нарушать имена файлов и других символов позднее во время повторного разбиения списка files. Поэтому лучиший способ будет:

Массивы:

 
    files=( $glob ); 
    echo "The first file is ${files[0]}"

Позиционные параметры:


    set -- $glob; 
    echo "The first file is $1"

Отложенное разложение:

 
    for file in $glob; 
    do 
      ...

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

Другим примером $(echo ..) является применения расширения ESC-последовательностей:


	unexpanded='var\tvalue'
	expanded="$(echo "$var")"

В этом случае, используйте взамен printf , который прекрасно ладит с ESC-последовательностями.

В конечном счете, если есть желание составить ряд символов через символ пробела, следует это делать лучше с for или printf :


    printf '%s\n' $glob   

SC2115

Пример сообщения:


	In bin/vhd-controls line 429:
	                        [  -d "$VHDMNT_DIRPATH/$mpoint_name"  ] &&  rm -rf "$VHDMNT_DIRPATH/$mpoint_name";   
                                                                                ^----------------------------^ SC2115: Use "${var:?}" to ensure this never expands to / .

В результате, исправляющий код :


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-02 04:16:33.738959200 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-02 04:28:45.588546549 +0300
	@@ -426,7 +426,7 @@
	                              break
	                      done
	-                     if rm -rf "$VHDFILE_PATH" ; then                      
	+                     if rm -rf "${VHDFILE_PATH:?}" ; then
	-                        [  -d "$VHDMNT_DIRPATH/$mpoint_name"  ] &&  rm -rf "$VHDMNT_DIRPATH/$mpoint_name";   
	+                        [  -d "$VHDMNT_DIRPATH/$mpoint_name"  ] &&  rm -rf "${VHDMNT_DIRPATH:?}/$mpoint_name";   
	                         echo "${vhd_bold}VHD enviroment:${vhd_normal} $VHDFILE was succesfully deleted."
	                      fi 
	                      ;;

Использование ${VHDMNT_DIRPATH:?} предотвратит удаление из корня RootFS точки монтирования $mpoint_name, когдаVHDMNT_DIRPATH не присвоено значение.

SC2120

Пример сообщения:


    In bin/activate line 22:
    vhdbash_newinst_exec(){  
    ^-- SC2120: vhdbash_newinst_exec references arguments, but none are ever passed.

Корректируемый код:


--- /path/to/storage/vhd-env/bin/activate	2025-01-31 10:28:36.824860300 +0300
+++ /path/to/vhdenv.git/bin/activate	2025-01-31 10:35:50.561017097 +0300
@@ -37,7 +38,7 @@
    echo "${vhd_bold}VHD enviroment${vhd_normal}: activity yet!"
 else
    if [ -f bin/activate-run  ] ; then  
-      vhdbash_newinst_exec  
+      vhdbash_newinst_exec  "$@" 
    else 
       echo "vhdenv error: You are out of the VHD enviroment."
    fi  

В функции, $1 и т.д. относится к параметрам функции, но скрипт использовался без параметров, поэтому в точке вызова был добавлен передаваемый аргумент "$@"

SC2140 и SC2180


    In bash-associative-arr.sh line 67:
    value="${ass2d_arr["0,1"]["value"]}"
           ^--------------------------^ SC2180: Bash does not support multidimensional arrays. Use 1D or associative arrays.
                            ^-- SC2140: Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?

Bash не поддерживает многомерные массивы и потому сопровождается SC2140, которая путается в количестве указанных двойных кавычек:


    declare -A ass2d_arr
    ...
    ass2d_arr["0,1"]=(["value"]="b" ["col"]="0" ["line"]="1")
    ...
    value="${${ass2d_arr["0,1"]}["value"]}"
    echo "The value for the element is: $value"

SC2141


	In bin/vhdenv-install line 66:
	 IFS=Backup_of_internal_field_separator
     ^-- SC2141: This IFS value contains the literal letter 'n'. For tabs/linefeeds/escapes, use $'..', literal, or printf.

В результате, исправляющий код:


    - Backup_of_internal_field_separator=IFS
	+ Backup_of_internal_field_separator=$IFS

SC2143


	In bin/vhd-controls line 338:
	                     if [ -z "$(losetup -l | grep "$VHDFILE_PATH")"  ] ; then
	                          ^-- SC2143: Use ! grep -q instead of comparing output with [ -z .. ].

В результате, исправляющий код:


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 19:25:17.758393500 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 19:28:05.310582078 +0300
	@@ -335,7 +335,8 @@
	                      fi     
	             ;;         
	             mount)
	-                     if [ -z "$(losetup -l | grep "$VHDFILE_PATH")"  ] ; then
	+#                     if [ -z "$(losetup -l | grep "$VHDFILE_PATH")"  ] ; then
	+                      if ! losetup -l | grep -q "$VHDFILE_PATH" ; then
	                         vhdenv_ldev=$(losetup -f) 
	                         if [ ! -b $vhdenv_ldev ] ; then
	                            echo "${vhd_bold}VHD enviroment:${vhd_normal} can't find the first unused loop device." 

Позволяет повысить эффективность за счет количества итераций grep до первого совпадения, снижаемых используемой опцией -q.

SC2145


    In bash-associative-arr.sh line 33:
    echo "All ordered pairs: ${!mpairs[@]}"
                         ^-----------^ SC2145: Argument mixes string and array. Use * or separate argument.

В принципе bash выполнит ${!mpairs[@]}, но shellcheck считает это грубой ошибкой, поэтому лучше вместо '${!mpairs[@]}' использовать :


    print "All values are associated with ordered pairs:"
    for pair in "${!mpairs[@]}" ;  
    do
       print "%s " "${mpairs[pair]}"
    done
    echo

SC2148

Результат. Корректируемый код:

   
    --- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 07:54:57.907348900 +0300 
    +++ /path/to/vhdenv.git/bin/activate-run	2025-01-31 13:50:23.554165390 +0300
    @@ -1,3 +1,4 @@
    +#!/bin/bash
    #This file must be used with "source vhd-env/bin/activate" from bash
    #You cannot run it directly.

В первую строку cкрипта activate добавлен шебанг #!/bin/bash.

SC2152

Пример сообщения:


    In bin/activate line 25:
              return retfail
                     ^-----^ SC2152: Can only return 0-255. Other data should be written to stdout.
    ...                 
    In bin/activate line 29:
              return retok 
                     ^---^ SC2152: Can only return 0-255. Other data should be written to stdout.

Результат. Корректируемый код:


	vhdbash_newinst_exec(){  
	         if [ ! -f "$1" ] ; then                            
	           echo "${vhd_bold}VHD enviroment error${vhd_normal}: bashrc file's path does not accepted." 
	-          return retfail
	+          return 1 
	         fi
	         if  [ -f bin/activate-run  ] && [ -f "$1" ] ; then
	           if sudo -u "$USER" VHDENV_BASHRCFILE="$1" bash bin/vhdbash-newinst-exec ; then 
	-             return retok 
	+             return 0 
	           fi
	         fi                 
	-        return retfail
	+        return 1
	 }      

SC2154


	In bin/vhd-controls line 271:
	           echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
	                                                                         ^-------^ SC2154: normal is referenced but not assigned.

В результате, исправляющий код:


    --- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 18:30:59.440021600 +0300
    +++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 18:54:46.485920568 +0300
    @@ -268,7 +268,7 @@
     #              mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
                    mount_ops=${VHDMOUNT_OPTIONS/,user/} 
                fi
    -           echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
    +           echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${vhd_normal} $VHDMNT_DIRPATH/$mpoint_name"
                if [[ $1 == mount  ]] ; then
                  echo "${vhd_bold}Mount options:${vhd_normal} $VHDMNT_DIRPATH/$mpoint_name, $mount_ops"
                fi

Ссылка не существующую на переменную normal была допущена при переименовании имени к vhd_normal .

SC2155


	In bin/activate-run line 85:
	      export VHDENV_ROOTDIR=$(pwd)
                 ^------------^ SC2155: Declare and assign separately to avoid masking return values
	
	In bin/activate-run line 87:
	      export  VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)
                  ^------------^ SC2155: Declare and assign separately to avoid masking return values.

В результате, исправляющий код:


    --- /path/to/storage/vhd-env/bin/activate-run	2025-01-31 18:44:13.745303200 +0300
	+++ /path/to/vhdenv.git/bin/activate-run	2025-01-31 18:48:47.967311927 +0300
    @@ -82,9 +82,10 @@
           vhd_deactivate
           exit 1
        elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f "$(pwd)"/bin/vhd-controls ] ; then
    -      export VHDENV_ROOTDIR=$(pwd)
    -      export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
    -      export  VHDENV_STORAGE=$(dirname "$VHDENV_ROOTDIR")
    +      VHDENV_ROOTDIR=$(pwd)
    +      VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
    +      VHDENV_STORAGE=$(dirname "$VHDENV_ROOTDIR")
    +      export VHDENV_ROOTDIR VHDENV_LIBPATH VHDENV_STORAGE
           echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename $VHDENV_LIBPATH) plug-in library" 
           source $VHDENV_LIBPATH
           if vhdenv_reinit ; then   

Декларация в качестве внешней переменной должно производится отдельно после присвоения значения переменной. Для локальной переменной эта ошибка может проявляться тоже :


	In bin/vhd-controls line 298:
	                     local vhdmnts=$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")
	                           ^-----^ SC2155: Declare and assign separately to avoid masking return values.

В результате, исправляющий код:


	--- /home/user/media/JMS678-satadrv/vhd-env/bin/vhd-controls	2025-02-01 18:54:54.243970800 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 19:08:04.970265993 +0300
	@@ -295,7 +295,8 @@
	                         printf "%-6s %-24s %s\n" "STATUS" "PLUGIN" "DESCRIPTION"
	                      fi
	                      local plugin_st="X ."
	-                     local vhdmnts=$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")
	+                     local vhdmnts 
	+                     vhdmnts="$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")"
	                      IFS=,
	                      for vhdmnt in $vhdmnts ;
	                      do

SC2162


	In bin/vhd-controls line 418:
	                             read -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
	                             ^--^ SC2162: read without -r will mangle backslashes.

В результате, исправляющий код:


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 21:59:42.822340100 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-02 03:43:59.563967471 +0300
	@@ -415,7 +415,7 @@
	                         fi 
	                      fi
		                      while true ; do
	-                             read -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
	+                             read -r -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
	                              if [ "$your_answer" != "y" ] && [ "$your_answer" != "n" ]  ; then
	                                 continue
	                              fi

Он будет предотвращать чтение управляющих символов в виде esc-последовательности:


	Are you sure? Can a file creating continue?(y/n):^[[A^[[A

SC2164


    In vhdenv-plugins/aarch64--glibc-cross/rules/activate-rule line 117:
         cd ~     
         ^--^ SC2164: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

Результат. Корректируемый код:


    151d153
    <     cd ~
    166a169
    >     cd "$HOME" || return 
   170,171c173,174

Рекомендуется использовать cd "$HOME" || return, потому что команда cd может завершиться с отказом из-за неправильно указанного пути, имени файла или директории, отсутсвия прав доступа, сломанной ссылки и т.д.

SC2181

Пример сообщения:


	In bin/vhd-controls line 261:
	              if [ $? -ne 0 ] ; then
	                   ^-- SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.

Полученный в результате код:


    --- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 14:15:39.859102200 +0300
    +++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 15:10:28.269944186 +0300
    @@ -257,8 +257,7 @@
                      echo "${vhd_bold}VHD enviroment :${vhd_normal} can't create the mount point: $VHDMNT_DIRPATH/$mpoint_name"
                      return $retfail
                   fi
    -              sudo chown "$(id -u)":"$(id -g)" "$VHDMNT_DIRPATH"/"$mpoint_name"
    -              if [ $? -ne 0 ] ; then
    +              if ! sudo chown "$(id -u)":"$(id -g)" "$VHDMNT_DIRPATH"/"$mpoint_name" ; then
                      return $retfail 
                   fi

Вместо косвенного if [ $? -ne 0 ] ;, широко используемого ранее, используется прямой способа определения результата выполнения команды.

Оценка нормального выполнения команды mycmd


    if mycmd ; then 
       echo " mycmd was succefully done."
    fi  

Оценка нормального выполнения команды mycmd


    if ! mycmd ; then
       echo " mycmd has returned an abnormal value"   
    fi

Примечание. Чтобы "не схватить" ошибку SC2152 возвращаемое значение командой должно быть в диапазоне 0,...,255 . При этом, возвращаемое нулевое значение будет являться "нормальным" возвращаемым значением, а все что больше или равно 1 и т.д. -- аномальным.

SC2209


    In bin/vhd-controls line 354:
                            mycmd=eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops"
                            ^-- SC2209: Use var=$(command) to assign output (or quote to assign string).

В результате, исправляющий код:


	--- /path/to/storage/vhd-env/bin/vhd-controls	2025-02-01 21:00:28.170068700 +0300
	+++ /path/to/repo/vhdenv.git/bin/vhd-controls	2025-02-01 21:02:34.796532787 +0300
	@@ -351,8 +351,8 @@
	                            return $retfail 
	                         fi
	-                        local mycmd
	-                        mycmd=eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops"
	-                        if mycmd ; then
	+                        if eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops" ; then
	                           eval "sudo losetup --detach  $vhdenv_ldev"
	                           echo "${vhd_bold}VHD enviroment error:${vhd_normal} can't associate $vhdenv_ldev's loop device with $(basename $VHDFILE_PATH) file."
	                           return $retfail

Вместо использования локальной переменной mycmd=$(eval "sudo -- ...") выполняемую команду подставил в if-statement, как показано выше.

SC2236

Пример сообщения :


	In bin/activate line 35:
	if [ ! -z "${VHDENV_STORAGE:-}" ] ; then
     	 ^-- SC2236: Use -n instead of ! -z.

Результат. Корректируемый код:


	--- /path/to/storage/vhd-env/bin/activate	2025-01-31 11:48:00.362123400 +0300
	+++ /path/to/repo/vhdenv.git/bin/activate	2025-01-31 11:54:50.424221414 +0300
	@@ -32,7 +32,7 @@
	         return 1
	 }             
	 
	-if [ ! -z "${VHDENV_STORAGE:-}" ] ; then
	+if [ -n "${VHDENV_STORAGE:-}" ] ; then
	    echo "${vhd_bold}VHD enviroment${vhd_normal}: activity yet!"
	 else
	    if [ -f bin/activate-run  ] ; then  

Произведена замена ! -z "${VHDENV_STORAGE:-}" на ! -z "${VHDENV_STORAGE:-}" .

 

Яндекс.Метрика