Bad substitution - ShellCheck says okay

ShellCheck doesn't find any issues with this script.

#!/bin/bash
# color_meanings: explain meanings of colors used in bash ls

eval "$(echo "no:fi:di:ln:pi:so:do:bd:cd:or:mi:su:sg:tw:st:ex" | sed -e 's/:/=/g; s/\;/\n/g')"
{
        IFS=:
        for i in $LS_COLORS
        do
                echo -e "\e[${i#*=}m$( x=${i%=*}; [ -n "${!x}" ] && echo "${!x}" || echo "$x" )\e[m"
        done
}

It prints out the colors and file descriptors, but then dozens of these messages follow:

/home/me/bin/color_meanings: line 9: *.tar: bad substitution
/home/me/bin/color_meanings: line 9: *.tgz: bad substitution
/home/me/bin/color_meanings: line 9: *.arc: bad substitution

You haven't told us what shell and operating system you're using...

The command:

sed -e 's/:/=/g; s/\;/\n/g'

doesn't need the <backslash> before the <semicolon>. And, <backslash> before <n> is not portable. On some versions of sed that character pair will be translated to a <newline> character. On a sed that conforms to the standards, it produces unspecified results (frequently just the letter <n> on some systems). But since there aren't any <semicolon>s in the input being passed to sed I guess it doesn't really matter anyway???

Your script has no definition for LS_COLORS ??? What are you hoping to have assigned to i on the various iterations through your loop?

And, of course, depending on what shell and OS you're using, the meaning of echo -e can vary widely.

When I run

echo $LS_COLORS

on my system I get this result

rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:

My guess is you get something similar. I believe the problem is the eval ine of the script does not have a substitution string available for all the additional entries, such as *.tar=01;31

1 Like

Guess again. On my system, the command:

echo "$LS_COLORS"

produces a single, empty line of output. The variable LS_COLORS is not defined in my environment.

It looks to me like your eval command is evaluating a command that assigns a long string of characters to the variable named no . And, after doing that, nothing ever uses the variable named no . What are you trying to do with the eval command?

Why did you wrap the IFS=: and the for loop in braces? In the script you have shown us, removing that pair of braces would not affect the output produced by your script.

Sorry! Still new at this game.

Desktop: Xfce 4.12.3 (Gtk 2.24.31) Distro: Ubuntu 18.04.1 LTS
~$ echo $BASH_VERSION 4.4.19(1)-release

When I enter env , LS_COLORS is displayed with exactly what TioTony described.
I removed the two backslashes mentioned, but still got the same error messages.

EDIT: I removed the two braces, and got the same results as before.

Hi Xubuntu56,
What do you want the output of the command substitution:

$(echo "no:fi:di:ln:pi:so:do:bd:cd:or:mi:su:sg:tw:st:ex" | sed -e 's/:/=/g; s/\;/\n/g')

to be?

What are you hoping the eval command will do with the output of that command substitution?

What output are you hoping your script will produce?

To answer the last question first, the script is producing the desired output: printing a legend for the different colors used by ls for each different type of file; i.e. di (directory) printed blue on a black background. I merely wanted it to do so without all the error messages.
So it is working, but with issues.

That is good to know, but please answer the other questions too. I'm not currently see any reason to keep the eval command in your script. If I knew why you had that line in your script, everything you're doing might suddenly make sense to me.

I changed the script around a little bit. The "*." in many of the $LS_COLORS items is causing the problem. I couldn't figure out how to fix it in the for loop echo -e so I used sed to change the values before entering the loop. The 7 in 7z was also causing some issues, at least on my system. Here is the end result that I think does what you want:

#!/bin/bash
# color_meanings: explain meanings of colors used in bash ls

CLEAN_COLORS=`echo $LS_COLORS | sed -e 's/\*\.//g'`
{
        IFS=:
        for i in $CLEAN_COLORS
        do
		echo -e "\e[${i#*=}m$( x=${i%=*};echo "$x")\e[m"
        done
}
1 Like

Assuming that you really want to see the *. 's in the output, but just don't want the diagnostics, see what happens if you try:

#!/bin/bash
OIFS=$IFS
IFS=':='
array=( $LS_COLORS )

IFS=$OIFS
printf '%s %s\n' "${array[@]}" | while read -r color sequence
do	printf '\e[%sm%s\e[m\n' "$sequence" "$color"
done
1 Like

Just switch off globbing temporarily ? In the OP's example:

        IFS=:
        set -f
        for i in $LS_COLORS
        do
                echo -e "\e[${i#*=}m$( x=${i%=*}; [ -n "${!x}" ] && echo "${!x}" || echo "$x" )\e[m"
        done
        set +f

--
shellcheck will check syntax, but it cannot know what is in the variables.

1 Like

The problem in your approach in post #1 is bash 's "indirection". man bash :

That indirection works (in fact supplies an empty string) if x holds a string that could be a valid variable name but fails on * or . (which can't be part of a valid variable name).
@Scrutinizer: that's why the -f option doesn't take effect - no pathname expansion attempted.

May I question the use of indirection in your script? What do you want to achieve by using it? Your simplified script, purged from indirection, looks like

IFS=':'
for i in $LS_COLORS
   do   echo -e "\e[${i#*=}m${i%=*}\e[m"
   done
[[0mrs[[m
[[01;34mdi[[m
[[01;36mln[[m
[[00mmh[[m
[[40;33mpi[[m
[[01;35mso[[m
[[01;35mdo[[m
[[40;33;01mbd[[m
[[40;33;01mcd[[m
[[40;31;01mor[[m
[[00mmi[[m
[[37;41msu[[m
[[30;43msg[[m
[[30;41mca[[m
[[30;42mtw[[m
[[34;42mow[[m
[[37;44mst[[m
[[01;32mex[[m
[[01;31m*.tar[[m
[[01;31m*.tgz[[m
 [[01;31m*.arc[[m

and works perfectly well. I showed the console codes as the colours don't translate to html too well.

EDIT: Added the IFS defnition to enable the script's success...

1 Like

@Don Cragun: good approach, but I had to modify it slightly to make it run on my linux host, like:

printf '%s\n' "${array[@]}" | while IFS== read -r color sequence
  do    printf '\e[%sm%s\e[m\n' "$sequence" "$color"
   done

(print just one array element at a time, and split the line read at the = sign.)

EDIT: I have to apologize; my fault when creating array . Don Cragun's code works flawlessly if array is defined the way he did in his post, with an element pair per colour entry.

1 Like

Hi Xubuntu56...

REMEMBER!
ShellCheck ONLY checks validity and syntax of the shell in use, and NOT ALL shells.
For that is is simply superb. However don't expect it to check validity and syntax of transient commands.

A deliberate simple example from ShellCheck.

As you can see, 'sed' and 'sleep' will create an error and has absolutely no idea what 'junk_command' is.
Hope this helps.

1 Like

Please allow me to explain: I had one directory highlighted in green upon running ls and I didn't know why. Researching the reason for all the different colors led me to a script I found which printed the names of the different types of files in their respective colors, but also produced exactly 112 error messages. I just wanted to get rid of the error messages. Next time I post, and the situation is similar, I will start with "I found a script...". I just re-read Neo`s forum rules, and didn't see anything which prohibits what I did, but even so, if I should stick to scripts I write myself, I will be glad to do that. I think this is a great community, and I want to remain a member in good standing!
@TioTony and @Don Cragun--those scripts worked well. Thanks.

There is no problem with posting code you found somewhere else, but when you do please state where you found it. And, it always helps to know both what is working and what is not working. If you just show us the error messages and don't tell us that you're also getting the output you wanted, we can waste a lot of time trying to figure out what output you do want and guessing whether or not you are getting the output you wanted.

The more we know about what you're doing, what you're seeing when you do it, and what you are trying to get to happen; the better chance you'll have of getting the answers you want and getting them faster.

Cheers,
Don

1 Like

I infer from this comment that my proposal falls short of success. Pls. try again with IFS=':' . I edited my post as well...

1 Like

First off: we were all newbies at some point and we don't bite (ah, well, usually... that is: mostly... make that: sometimes... bets are off when the moon is full....).

You might want to read the all-time classic How to Ask Questions the Smart Way.

Or, my own take on it: What to include in a post

I hope this helps.

bakunin

1 Like

@RudiC
I'm away from my main setup, but your edited version worked like a charm on my travel laptop.:b:
@bakunin
I shall read the "Smart Way". I liked your shorter take on it.