How to check the ls version - shell

This topic is about the util 'ls'
The BSD version uses the parameter '-G' to color up the output,
while the Linux version uses parameter '--color'
Also the environment variable to set the colors is different:
BSD: $LSCOLORS
Linux: $LS_COLORS
But now the problem is: I want to determine which version is installed (using a small Shell script), so I can set alias ls and the environment appropriate in my .bachrc file.

As I mentioned above this seems to me to be the handiest method
if ls --color -d . >/dev/null 2>&1; then
GNU_LS=1
elif ls -G -d . >/dev/null 2>&1; then
BSD_LS=1
else
SOLARIS_LS=1
fi
I've essentially this in my l script, which I use on various platforms to tweak ls output as I like

Just run 'ls' and see whether it throws an error, e.g. on my mac:
$ ls --color 1>/dev/null 2>&1
$ echo $?
1
Whereas
$ ls -G 1>/dev/null 2>&1
$ echo $?
0
Indicating -G is supported, but --color is not.

Ironically, the --version switch Kimmo mentions is not supported on most BSD systems :-)
Writing a portable configuration file for your particular setup can be a Herculean task. In your case, if you're sure your .bashrc is going to be used only on GNU/Linux and on a BSD system, you can check for switches that exist in one of the ls' but not in the other: for example, -D doesn't seem to be an accepted switch by ls on my BSD machines (FreeBSD and Mac OS X), whereas it is for GNU ls. Conversely, -P is accepted on BSD, but not on GNU/Linux. Knowing this, you can distinguish between the two ls' and set up environment variables accordingly.

$ ls --version
ls (GNU coreutils) 6.10
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Richard Stallman and David MacKenzie.

combining the methods described, here's an easy way to use a bash function instead of an alias in order to make colors work regardless of if you are using BSD or GNU ls.
ll () {
if ls --version &>/dev/null; then
ls --color=auto -lahtr
else
ls -Glahtr
fi
}
inspired by a particular conda env recipe pulling in GNU ls on my macOS system where my ls aliases were all hard-coded for stock BSD ls only.

Related

`set -o posix` Not Working in Bash 5.0.17 on WSL2 Ubuntu 20.04

As mentioned in the title, the set -o posix option does not appear to be working in GNU/Linux bash 5.0.17 (I'm not sure it's specifically related to WSL2 Ubuntu 20.04 or anything, but noted it in case it is working for others on their machines).
I can turn it on and off:
$ set -o |grep posix
posix off
$ set -o posix
$ set -o |grep posix
posix on
$ set -o posix
$ set -o |grep posix
posix off
$ set -o posix
$ set -o |grep posix
posix on
but, for example, when on I am able to do the following
$ set -o posix
$ set -o |grep posix
posix on
$ type cd
cd is a shell builtin
$ cd /
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
$ cd() { :; }
$ type cd
cd is a function
cd ()
{
:
}
$ cd ~
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
$ unset -f cd
$ cd ~
$ ls
Desktop Documents Downloads Music Pictures Public Templates Videos
I am able to override all special builtins (including builtin and type!). Also, I am not root, but just in my user account. Does anyone know what I am doing wrong?
UPDATE:
#Shawn, #oguzismail, and #Dan all give the correct answer below: cd is a regular builtin. I wanted to add some clarification as I think it will be important for learners like me:
#oguzismail correctly points to the section of Classic Shell Scripting that explains that cd is a regular built-in:
For this reason, you can write a function named cd, and the shell will
find your function first, since cd is a regular built-in
Section 7.9 (pg. 261 in Kindle Edition)
My confusion came from the statement in the summary:
Built-in commands exist either because they change the shell’s
internal state and must be built-in (such as cd), or for efficiency
(such as test).
(Section 7 Summary, pg. 264 in Kindle Edition)
Here, cd is a builtin, but as the author states previously, it is a regular builtin. It has to be a command built into the shell because the command changes the internal state of the shell (i.e. it changes the current/present working directory of the shell), but it can be either a regular or special builtin. As #Dan mentions, the POSIX IEEE Std 1003.1-2017 standard, Section 2.14 defines it as a regular builtin. (For at least one reason why, Section 7.9 of Classic Shell Scripting shows that doing so permits the programmer to customize its behavior.)
Be Warned Though! (NB = Nota Bene): If you do choose to overwrite cd with a user-defined function, use command cd within the function definition where you want to actually change directories. The function command skips looking for functions in the POSIX-defined search order of special built-ins --> functions --> regular built-ins --> external commands on $PATH. Otherwise, if you use cd within your user-defined function cd, you will most likely end up with an infinite recursion loop. (Classic Shell Scripting also covers this point.)
The key here is what bash considers a special builtin to be. I had to dig into the source to find a list:
/* The Posix.2 so-called `special' builtins. */
char *special_builtins[] =
{
":", ".", "source", "break", "continue", "eval", "exec", "exit",
"export", "readonly", "return", "set", "shift", "times", "trap", "unset",
(char *)NULL
};
They're also described in the POSIX documentation as being distinct from regular built-ins.
And indeed...
$ bash --posix
bash-4.4$ break() { :; }
bash: `break': is a special builtin
bash-4.4$ type break
break is a special shell builtin
bash-4.4$ type cd
cd is a shell builtin
Note the different output from type for break and cd, because the latter isn't in that list.
set -o posix works fine:
$ set -o posix
$ set () { :; }
bash: `set': is a special builtin
cd is not a special built in, nor is builtin or type.
The list of "special builtins" is clearly defined in the posix specification.
A "special built-in" is distinct from any other command which a shell may or may not implement as a built-in.

How to see what /bin/sh points to

I was reading about the differences between /bin/sh and /bin/bash and came across this interesting question/answer: here
I made a comment to the answer asking this same question in my title to which he replied with an updated answer:
How can you find out what /bin/sh points to on your system?
The complication is that /bin/sh could be a symbolic link or a hard
link. If it's a symbolic link, a portable way to resolve it is:
> % file -h /bin/sh
> /bin/sh: symbolic link to bash
If it's a hard link,
try
> % find -L /bin -samefile /bin/sh
> /bin/sh
> /bin/bash
I tried this and had trouble so I thought I would make a separate question.
My results from the linked answer:
>file -h /bin/sh
>/bin/sh: executable (RISC System/6000) or object module
>find -L /bin -samefile /bin/sh
>find: bad option -samefile
What am I missing?
I'm running AIX:
>oslevel -s
7100-03-03-1415
If you need to programatically test if they are the same, you can use stat to query the inode of /bin/sh and compare with the inode of /bin/bash.
if [ $(stat -L -c %i /bin/sh) -eq $(stat -L -c %i /bin/bash) ]; then
.....
fi
If you just need to see with your eyes if they are the same run the stat commands and see if they return the same inode number.
stat -L -c %i /bin/sh
stat -L -c %i /bin/bash
Since you are only searching through bin anyway, you can bypass find entirely and just check if sh and bash are hard links to the same file:
test /bin/sh -ef /bin/bash
OR
[ /bin/sh -ef /bin/bash ]
This is not as reliable as running find on all the possibilities, but it's a good start. While AIX find doesn't support -samefile, it does support -exec, which can be combined with the command above to simulate the same functionality:
find -L /bin -exec test /bin/sh -ef '{}' ';'
Check for GNU Bash
I'm going to answer your question in a different way, because it's actually simpler to find out if sh is GNU Bash (or something else that responds to a --version flag) than it is to chase inodes. There's also the edge case where the shell is renamed rather than linked, in which case mapping links won't really help you find an answer.
For example, to interrogate /bin/sh on macOS:
$ /bin/sh --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
Alternatively, you can grep (or similar) for the string bash to capture the exit status. For example:
# Close stderr in case sh doesn't have a version flag.
if sh --version 2>&- | grep -qF bash; then
echo "sh is bash"
else
echo "sh isn't bash"
fi
Of course, /bin/sh could be some other shell besides bash or the original bourne shell, but that's outside the scope of your original question. However, many shells such as ksh and tcsh also support the version flag, so judicious use of a case statement could help you extend the test to determine exactly which shell binary /bin/sh really is.
As the answer you commented on says, -samefile isn't a standard feature of find. Neither is -inum, which searches based on an explicitly given inode number. But if your find supports that, use it.
POSIX ls supports -i, which prints the inode numbers. If find doesn't have -inum, all you need to do is go through all plausible files /bin/sh could be a hard link with...
Though before that, you could check if the file even has any other hard links, ls -l is required to show the number of links:
$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1029624 Nov 5 23:22 /bin/bash
^ here
Of course, there's no requirement for /bin/sh to be linked to anything. It could be just another program. (Or even an identical copy of some of other file, though I don't think that's very likely.)
You can just use :
ls -l /bin/sh
You can also do readlink -f /bin/sh. For me it points to /bin/dash.
I can not really see the point of this exercise.
/bin/sh is a POSIX shell and bash has extensions to the POSIX standard that /bin/sh does not support.
If you are writing a POSIX shell script, use /bin/sh. If you are writing a bash shell script, use bash.
Do not try to write a bash script that will be executed with /bin/sh, and don't try to programatically try to determine the abilities of the current shell based on what /bin/sh (or the current interpreter) is linked to. Instead, make the script executable and use the correct #!-line for the script.

Why can't I pass these parameters to my command?

B=master U='my.email#email.com' curl -u "${U}" "https://bitbucket.org/myteam/pod-dev/raw/${B}/install.bash"
I get asked:
Enter host password for user '':
See, the "U" is missing.
And also curl is performed:
GET /myteam/pod-dev/raw//install.bash HTTP/1.1
Also here, the B is missing (branch)
macOS Sierra 10.12.2
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
Variables assignments like you are using them are part of the command. In your case the shell will try to expand "${A}", "${B}" before they have been actually set - during parsing the command.
You can separate the variable assignments and the actual command by ;:
B=master; U='my.email#email.com'; curl -u "${U}" "https://bitbucket.org/myteam/pod-dev/raw/${B}/install.bash"
That way they are 3 separate commands.
bash will try to expand the variables first, but you haven't set them at this point. See for example:
$ x=foo echo "$x"
>
$ x=foo; echo "$x"
> foo
Try like this instead of your command:
export B=master; export U='my.email#email.com' ; curl -u "$U" "https://bitbucket.org/myteam/pod-dev/raw/$B/install.bash"

mkdir -pv not verbose

If I run mkdir -pv, the -p works, but I do not get verbose output; however, if I run just mkdir -v, the verbose output appears as expected. Also potentially of note, the longform of -v does not seem to work at all.
From my testing:
mkdir -p a/b/c: creates a/, a/b/, and a/b/c/, gives no output to terminal (as expected)
mkdir -v d: creates d/ and outputs mkdir: created directory 'd' (as expected)
mkdir -pv e/f/g: creates e/, e/f/, and e/f/g/, gives no output to terminal (why?)
mkdir --verbose h: gives a illegal option -- - error (why?)
Update: I filed a bug report with Apple for this issue, and received the following reply:
Their answer that "-v is not applicable" does not make much sense to me, since mkdir -v works just fine, but since there are various workarounds and I am no longer even using OSX, I don't think it's worth pursuing any further to me.
Macs use BSD-based code that is (mostly) POSIX compliant but (mostly) without GNU extensions (such as double-dash long options). The man page does document -v and -p, and -p works but does seem to suppress the -v option (which is probably when it is most useful).
One of your options is to file a bug with the Darwin or BSD teams, or with Apple. That's the way it is; it is arguably not the way it should be. (GNU mkdir supports -v and prints the directories it creates when used with -p, which makes more sense, and supports the 'it is a bug' contention.)
With thanks to SnoringFrog:
Another option is to install and use the GNU mkdir command on OSX. It is part of GNU coreutils and you could install it as explained in How to replace Mac OS X utilities with GNU core utilities at Ask Different. Then, you could alias mkdir to point to gmkdir to get the expected behavior (assuming you don't use --default-names when installing the GNU tools).
You could also do this if logging is critical (since the -v flag works in the simple case):
mkdir -v path &&
mkdir -v path/to &&
mkdir -v path/to/{destination1,destination2,etc} ;
Think Different or Think Again?!

Pipes inside backticks fail under MinGW

When I try to run some Bash scripts on a Windows XP machine using MinGW, I get the following error:
./autogen.sh: pipe error: No such file or directory
I have localised the problem to Bash lines such as the following, which have a pipe inside backticks:
__copyright="`grep Copyright ./autogen.sh | head -1`"
However, pipes not inside backticks work just fine:
grep Copyright ./autogen.sh | head -1
All the programs you expect (sh, head, grep) are available and run happily on the command line and in Bash.
What should I do to resolve this error? I cannot run most Bash build tools without it.
$ msysinfo | head -3
msysinfo-1.3: Send this to the MSYS support list:
MSYS 1.0.11(0.46/3/2) 2004-04-30 18:55 i686 unknown; targ=MINGW32
$ echo '__copyright="`grep Copyright ./autogen.sh | head -1`" && echo $__copyright' >test.sh
$ cat test.sh
__copyright="`grep Copyright ./autogen.sh | head -1`" && echo $__copyright
$ sh test.sh
# Copyright 2005, 2006, 2007, 2008 by
For this test, I copied autogen.sh from some place like... randomly... http://svn.ghostscript.com/ghostscript/tags/freetype-2.3.7/autogen.sh
Probably this means your question needs more information.
But... sometimes when I run into snarly scenarios, it can help to enclose the breaking code in ( ). Technically, also, you do not need " " around backticks, and that complicates some use, but your example does not seem problematic.
I really hate to propose the following as it seems completely unnecessary:
$ for item in `grep Copyright ./autogen.sh`;
do
__copyright="$__copyright $item";
done;
echo $__copyright
What is more weird is that your error message seems to imply autogen.sh itself generated the error as though autogen.sh was grepping itself so I did:
$ sh test.sh
__copyright="`grep Copyright ./test.sh | head -1`" && echo $__copyright
after modifying test.sh to grep itself, and even that worked.
It therefore sounds like a sequencing issue and not a backtick issue, you know, which came first, the chicken or the egg?
Is autogen.sh trying to read its own Copyright comment? One way to let comments serve as data is to wrap them in here documents:
_Copyright ()
{
cat <<-END_OF_TEXT
# Copyright 2012, me
END_OF_TEXT
}
_Copyright

Resources