Why a command option "-f" be taken as a command by bash - bash

I run the command copied from the time's manual, but it complained -f command not found. Why it recognized -f as a command?
$ time -f "%E real,%U user,%s sys" ls -Fs
-f: command not found
real 0m0.152s
user 0m0.108s
sys 0m0.040s
I doubt the shell and decide to try another shell. In the above, the shell is bash. I switched to dash and found time run correctly now.
My system is Linux Mint 13.

time is a built-in command in bash and zsh. If you'd like to execute the external command instead, you should call /usr/bin/time -f ....

You can use the bash built-in command to suppress shell functions and built-ins:
command time -f "%E real,%U user,%s sys" ls -Fs
(Longer, but a little more explicit, than using \time.)

Bash includes a stripped-down version of time. Use /usr/bin/time instead.

Added to other answers, you can override the built-in command by \.
\time -f "%E real,%U user,%s sys" ls -Fs

Related

Can't run "compgen -c" from perl script

I want to check if a command exists on my machine (RedHat) inside a perl script.
Im trying to check if compgen -c contains the desired command, but running it from inside a script just gives me an empty output. Other commands work fine.
example.pl:
my $x = `compgen -c`;
print $x;
# empty output
my $y = `ls -a`;
print $y;
# .
# ..
# example.pl
Are there possible solutions for this? Or is there a better way to check for commands on my machine?
First, Perl runs external commands using /bin/sh, which is nowadays a link to a shell that is a default-of-sorts on your system. Much of the time that is bash, but not always; on RedHat it is.
This compgen is a bash builtin. One way to discover that is to run man compgen (in bash) -- and the bash manual pops up. Another way is type as Dave shows.
To use builtins we generally need to run an explicit shell for them, and they have a varied behavior in regards to whether the shell is "interactive" or not.† I can't find a discussion of that in bash documentation for this builtin but experimentation reveals that you need
my #completions = qx(bash -c "compgen -c")
The quotes are needed so to pass a complete command to a shell that will be started.
Note that this way you don't catch any STDERR out of those commands. That will come out on the terminal, and it can get missed that way. Or, you can redirect that stream in the command, by adding 2>&1 (redirect to STDOUT) at the end of it.
This is one of the reasons to use one of a number of good libraries for running and managing external commands instead of the builtin "backticks" (the qx I use above is an operator form of it.)
† This can be facilitated with -i
my #output_lines = qx(bash -i -c "command with arguments")
It's because compgen is a bash built-in command, not an external command. And when you run a command using backticks, you get your system's default shell - which is probably going to be /bin/sh, not bash.
The solution is to explicitly run bash, using the -c command-line option to give it a command to run.
my $x = `bash -c compgen -c`;
From a bash prompt, you can use type to see how a command is implemented.
$ type ssh
ssh is /usr/bin/ssh
$ type compgen
compgen is a shell builtin

Run bash command from windows Command line (WSL)

I have installed WSL on Windows 10 Pro.
And I need to execute bash commands from Windows Command Line like this:
bash -c ll
Expected: ll command output in Command Line console
In practice: /bin/bash: ll: command not found
But its work for some commands like ls or apt.
Please, see :
What could be the problem?
ll is a common alias (for ls -alF in WSL; defined in the default .bashrc). Depending on how you invoke bash will determine whether the scripts which set up your system aliases are run. See the INVOCATION section of the bash manual.
You can use bash -i -c ll to invoke bash in an appropriate way for WSL.
Apparently ll is an alias you defined in some of your configuration files. You should start bash as follows:
bash -ilc ll
Depending on where you defined the aliases, you can omit the -i or -l flag.
ll is usually an alias of 'ls -l and can't (shouldn't) be used in script or command line.
Instead use directly the command itself: bash -c 'ls -l'.
To see if a certain command is an alias use the command type:
type ll
ll is aliased to `ls -l'

Which shell does Perl 6's shell() use?

Perl 6's shell sends commands to the "shell" but doesn't say what that is. I consistently get bash on my machine but I don't know if I can rely on that.
$ perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
$ csh
% perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
% zsh
$ perl6 -e 'shell( Q/echo $SHELL/ )'
/bin/bash
That's easy enough on Unix when it's documented, but what about cmd.exe or PowerShell on Windows (or bash if it's installed)? I figure it's the cmd.exe but a documented answer would be nice.
Looking at the source, rakudo just calls /bin/sh -c on non-windows and uses %*ENV<ComSpec> /c on windows.
dash (installed as /bin/sh on many systems), doesn't set $SHELL, nor should it. $SHELL isn't the name of the parent process; it's the name of the shell that should be used when an interactive shell is desired.
To get the name of the parent process, you could use the following on some systems:
echo "$0"
or
# Command line
perl -e'$ppid=getppid(); #ARGV="/proc/$ppid/cmdline"; CORE::say "".<>'
or
# Program file
perl -e'$ppid=getppid(); CORE::say readlink("/proc/$ppid/exe")'
You'll find you'll get /bin/sh in all cases.

Run command as bash from POSIX shell

I have a quick question
I have a posix shell but I need to run a bash command.
Think
root#home:~# sh
# /bin/bash /bin/ls
However, when I do that, I get
/bin/ls: /bin/ls: cannot execute binary file
I'm sure I'm missing something simple, but I'm not sure what it is. Any help? I also need to do it in one line.
Use the -c argument to specify a command that you want to be ran by the other shell:
" -c Read commands from the command_string operand"
bash -c "ls"
I came up with a workaround
echo '#!/bin/bash\--comamnd--; chmod ugo+x /tmp/script.sh; /tmp/script.sh

Difference between pgrep in sh and bash

Here is a test:
$ bash -c "pgrep -f novalidname"
$ sh -c "pgrep -f novalidname"
11202
Why is pgrep giving output when run from sh? (As far as I can see, there are no processes on my computer that is named novalidname)
It's probably a timing issue and pgrep finds itself, as you're issuing it with -f and novalidname is present in the command line. Try with -l to confirm.
The actual explanation:
Regardless of flags, pgrep never returns its own PID.
If you execute bash -c with a simple command, then bash will exec the command rather than creating a redundant subshell to execute it in. Consequently, bash -c "pgrep -f blah" will replace the bash process with a pgrep process. If that pgrep process is the only process whose command line includes blah, then pgrep will not display any PIDs (as per 1).
dash does not perform the above optimization. (zsh and ksh do.) So if on your system, sh is implemented with dash, then sh -c "pgrep -f blah" will result in two processes being executed -- the sh process and the pgrep child -- both of which contain blah in their command lines. pgrep will not report itself, but it will report its parent.
That's one thing (finding itself because of delay) see also:
$ ps ax | grep novalidname
Here it usually shows as well. (on Ubuntu does for me. (under bash)
The other thing is what is /bin/sh bound to?
On most Linux distros /bin/sh is a soft link to default shell which is usually actually bash, but can be any other shell.
The time difference that causes grep/pgrep to show itself may be introduced by finding a soft link location (hm, odd) or some other shell is bound to /bin/sh which executes slightly different than bash, thus causing the delay needed for process to show in pgrep.
Also, bash will firstly try to source ~/.bashrc and load its history, while /bin/sh will do what will do. In .bashrc can be pgrep defined as alias in another way which may also affect the difference.
To see where /bin/sh points to do:
$ readlink -e /bin/sh
Or just run sh to see what will show up. :D

Resources