What does the '-' operator actually do in Linux? - linux

I see the - operator behaving in different ways with different commands.
For example,
cd -
cds to the previous directory, whereas,
vim -
reads from stdin
So I want to know why the - operator is behaving in 2 different ways here. Can someone point me to some detailed documentation of the - operator?

It is not an operator, it is an argument. When you write a program in C or C++ it comes as argv[1] (when it is the first argument) and you can do whatever you like with it.
By convention, many programs use - as a placeholder for stdin where an input file name is normally required, and stdout where an output file name is expected. But cd does not require reading a file stream, why should it need stdin or stdout?
Extra: here below is the excerpt from vim's main.c that parses arguments that begin with -: if there is no additional character it activates STDIN input.
else if (argv[0][0] == '-' && !had_minmin)
{
want_argument = FALSE;
c = argv[0][argv_idx++];
#ifdef VMS
...
#endif
switch (c)
{
case NUL: /* "vim -" read from stdin */
/* "ex -" silent mode */
if (exmode_active)
silent_mode = TRUE;
else
{
if (parmp->edit_type != EDIT_NONE)
mainerr(ME_TOO_MANY_ARGS, (char_u *)argv[0]);
parmp->edit_type = EDIT_STDIN;
read_cmd_fd = 2; /* read from stderr instead of stdin */
}

The dash on its own is a simple command argument. Its meaning is command dependent. Its two most usual meanings are 'standard input' or (less often) 'standard output'. The meaning of 'previous directory' is unique to the cd shell built-in (and it only means that in some shells, not all shells).
cat file1 - file2 | troff ...
This means read file1, standard input, and file2 in that sequence and send the output to troff.
An extreme case of using - to mean 'standard input' or 'standard output' comes from (GNU) tar:
generate_file_list ... |
tar -cf - -T - |
( cd /some/where/else; tar -xf - )
The -cf - options in the first tar mean 'create an archive' and 'the output file is standard output'; the -T - option means 'read the list of files and/or directories from standard input'.
The -xf - options in the second tar mean 'extract an archive' and 'the input file is standard input'. In fact, GNU tar has an option -C /some/where/else which means it does the cd itself, so the whole command could be:
generate_file_list ... |
tar -cf - -T - |
tar -xf - -C /some/where/else
The net effect of this is to copy the files named by the generate_file_list command from under the 'current directory' to /some/where/else, preserving the directory structure. (The 'current directory' has to be taken with a small pinch of salt; any absolute file names are given special treatment by GNU tar — it removes the leading slash — and relative names are taken as relative to the current directory.)

It depends on the program it's being used in. It means different things to different programs.

I think different program use different convention. manpages shows how each program interpret -, here is man bash
-
At shell startup, set to the absolute pathname used to invoke the shell
or shell script being executed as passed in the environment or argument list.
Subsequently, expands to the last argument to the previous command, after expansion.
Also set to the full pathname used to invoke each command executed and placed
in the environment exported to that command. When checking mail, this parameter
holds the name of the mail file currently being checked.
and man vim
- The file to edit is read from stdin. Commands are read from stderr,
which should be a tty.

Related

Where is the name of the file supplied from in piped tar command?

Consider the command
The link to this. Scroll a bit down, and you will find it.
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
How this command is explained in the documentation:
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Move entire file tree from one directory to another
# [courtesy Alan Cox <a.cox#swansea.ac.uk>, with a minor change]
# 1) cd /source/directory
# Source directory, where the files to be moved are.
# 2) &&
# "And-list": if the 'cd' operation successful,
# then execute the next command.
# 3) tar cf - .
# The 'c' option 'tar' archiving command creates a new archive,
# the 'f' (file) option, followed by '-' designates the target file
# as stdout, and do it in current directory tree ('.').
# 4) |
# Piped to ...
# 5) ( ... )
# a subshell
# 6) cd /dest/directory
# Change to the destination directory.
# 7) &&
# "And-list", as above
# 8) tar xpvf -
# Unarchive ('x'), preserve ownership and file permissions ('p'),
# and send verbose messages to stdout ('v'),
# reading data from stdin ('f' followed by '-').
#
# Note that 'x' is a command, and 'p', 'v', 'f' are options.
#
# Whew!
There are a couple of things which I don't understand in the explanation given above-
In the third step it states f - designates the target file as standard output, but there is nothing in output right now, while creating the archive. From where is the name of the file supplied?
In the eighth step it states it reads data from standard input, but I didn't give any input. Is there any input left in the stream?
This command works fine, but I am a little confused about how it works.
The explanation you quoted from the code is remarkably good. I wish every script I read (or wrote!) was documented so well.
In the 3rd step it states f - designates the target file as stdout, but there is nothing in output right now, while creating archive, from where is the name of file supplied?
There is no file name. The archive data are written to stdout, the process's standard output stream. If that were not piped into another program then it would be displayed on the screen.
In the 8th step it states, it reads data from stdin, but I didn't give any input, is there any input left in the stream?
The output (to its stdout) of the first tar command is piped into (the stdin of) the second tar command, as mentioned at step 4 of the documentation. You can't give the second tar any input directly, because it is reading its input from the pipe, not the keyboard or any regular file.
In the 3rd step it states f - designates the target file as stdout, but there is nothing in output right now, while creating archive, from where is the name of file supplied?
stdin and stdout are two data streams that are created by OS for every process (a command that is run). The process may or may not use these streams but OS will create them anyways.
This is where the first process is writing its output to.
In-memory data streams need not have a file name, these are not regular files on disk.
In the 8th step it states, it reads data from stdin, but I didn't give any input, is there any input left in the stream?
There are 2 processes separated by a pipe | and you can visualize it as:
(cd /source/directory && tar cf - . ) -> [stdout] -> | -> [stdin] -> (cd /dest/directory && tar xpvf -)
So, the first process on left hand side writes the output to its own stdout. The pipe | is OS level plumbing that pumps data from stdout of previous process to stdin of next process. The process on right hand side reads data from its stdin.
Also, like the pipe | pattern, the dash - is a common cli pattern to provide in place of a file, that tells the command to read write data to/from stdin/stdout instead of a file.
The first parenthesis does the following: it changes directory to /source/directory/ and generates a tar file whose content is the current directory "." and sends it to the standard output.
The second parenthesis changes directory to /dest/directory/ and extracts there the archive it reads from its standard input.
I.e., you tar the content of "/source/directory" and you untar it in "/dest/directory" without using an intermediate file to do so, just a pipe "|" to make the junction between the two commands.
NB: the parenthesis creates a subprocess, so you've got a subprocess executing the tar c and another one executing the tar f running at the same time, the output of one sub-process being fed to the second subprocess.

Conversion of dpn, type commands from windows to bash

I recently try linux (from windows), and I find it difficult to process my following windows command to linux bash.
The windows command was:
set /p cutoff=Set BLAST E-Value Cutoff[1e-]:
for %%F in (*.fa) do program.exe -parameter1 %%F -parameter2_cutoff 1e-%cutoff% -output_file %%~dpnF.fas & type %%F %%~dpnF.fas > %%~dpnF.txt
This script takes a numeric value from user and uses it to run a program in every .fa files on a folder with the desired cutoff. Here %%~dpnF takes only the filename (without file extension). In this very script, I join the content of each input file (.fa) and its generated output (.fas) and finally merge them in final output (.txt). Here, for each Input file, there will be a final output file.
To run it in ubuntu , I try
echo "Set BLAST E-Value Cutoff[1e-]:"
read cutoff
for $f in *.fa; do program -parameter1 $f -parameter2_cutoff 1e-$cutoff -output_file $~dpnF.fas & cat $f $~dpnF.fas > $~dpnF.txt; done
Immediately it shows that linux is not supporting dpn type of command in windows and also the scripts terminates abruptly, showing no output.
Although I understand the different file extensions are not very meaningful in linux, but I have to keep it this way for other programs to process them.
I appreciate any type of help.
Thanks
The sequence %~dpn is used to get:
%~d - The drive
%~p - The path
%~n - The file name
Check the meaning of all expansions here.
The drive has no meaning in Linux. The path, full or partial, could be extracted with the command dirname and the filename could be extracted with the command basename.
The sequence %%~dpn means to get the whole pathname from root (/).
In fact, you do not need that in Linux, if a list of files was created with *.f, the list of files will be relative to the "present working directory" (command pwd), no need to extend them.
And to strip the extension from a filename, use ${f%.*}.
That cuts the string in "$f" at the last dot . and anything that follows *.
Then just add the extension you want: ${f%.*}.fas
Also, the character & has the meaning of "run the previous command in the background", which is not what you want.
And finally, the for $f should be replaced by for f.
This is a cleaner translation:
echo "Set BLAST E-Value Cutoff[1e-]:"
read cutoff
for f in *.fa; do
program -parameter1 "$f" \
-parameter2_cutoff "1e-$cutoff" \
-output_file "${f%.*}.fas"
cat "$f" "${f%.*}.fas" > "${f%.*}.txt"
done

'less' the file specified by the output of 'which'

command 'which' shows the link to a command.
command 'less' open the file.
How can I 'less' the file as the output of 'which'?
I don't want to use two commands like below to do it.
=>which script
/file/to/script/fiel
=>less /file/to/script/fiel
This is a use case for command substitution:
less -- "$(which commandname)"
That said, if your shell is bash, consider using type -P instead, which (unlike the external command which) is built into the shell:
less -- "$(type -P commandname)"
Note the quotes: These are important for reliable operation. Without them, the command may not work correctly if the filename contains characters inside IFS (by default, whitespace) or can be evaluated as a glob expression.
The double dashes are likewise there for correctness: Any argument after them is treated as positional (as per POSIX Utility Syntax Guidelines), so even if a filename starting with a dash were to be returned (however unlikely this may be), it ensures that less treats that as a filename rather than as the beginning of a sequence of options or flags.
You may also wish to consider honoring the user's pager selection via the environment variable $PAGER, and using type without -P to look for aliases, shell functions and builtins:
cmdsource() {
local sourcefile
if sourcefile="$(type -P -- "$1")"; then
"${PAGER:-less}" -- "$sourcefile"
else
echo "Unable to find source for $1" >&2
echo "...checking for a shell builtin:" >&2
type -- "$1"
fi
}
This defines a function you can run:
cmdsource commandname
You should be able to just pipe it over, try this:
which script | less

What does this bash script command mean (sed - e)?

I'm totally new to bash scripting but i want to solve this problem..
the command is:
objfil=`echo ${srcfil} | sed -e "s,c$,o,"`
the idea about the bash script program is to check for the source files, and check if there is an adjacent object file in the OBJ directory, if so, the rest of the program runs smoothly, if not, the iteration terminates and skips the current source file, and moves on to the next one.. it works with .c files but not on the headers, since the object filenames depend on .c files.. i want to write this command so it checks the object files not just the .c but the .h files too.. but without skipping them. i know i have to do something else too, but i need to understand what this line of command does exactly to move on. Thanks. (Sorry for my english)
UPDATE:
if test -r ${curOBJdir}/${objfil}
then
cp -v ${srcfil} ./SAVEDSRC/${srcfil}
fdone="NO"
linenums=ALL
else
fdone="YES"
err="${curOBJdir}/${objfil} is missing - ${srcfil} skipped)"
echo ${err}
echo ${err} >>${log}
fi
while test ${fdone} == "NO"
do
#rest of code ...
here is the rest of the program.. i tried to comment out the "test" part to ignore the comparison just because i only want my script to work on .h files, but without checking the e.g abc.h files has an abc.o file.. (the object file generation is needed because the end of the script there's a comparison between the hexdump of the original and modified object files). The whole script is for changing the basic types with typedefs like int to sint32_t for example.
This concrete command will substitute all c's right before line-end to o:
srcfill=abcd.c
objfil=`echo ${srcfil} | sed -e "s,c$,o,"`
echo $objfil
Output:
abcd.o
P.S. It uses a different match/replace separator: default is / but it uses ,.

What do the symbols -, < and > mean?

I read this example in a book:
cp /bin/cat proj33
echo -n x | ./proj33 - pipe33a > pipe33b &
./proj33 <pipe33b >pipe33a &
What do the symbols -, > and < mean?
The > is I/O redirection; it sends the standard output of the command (./proj33) to the file pipe33b.
The < symbol is also I/O redirection; it sends the standard input of the command (./proj33 again) from the file pipe33b.
The - is just an argument. It is often treated by commands as an indication to read standard input instead of a file. In this context, it is likely that proj33 normally requires a file as its first argument, but it reads from the pipe when the argument is -. Sometimes, the - is used to indicate standard output. In extreme cases, you can get one - indicating standard input and one indicating standard output on a single command. With GNU tar, you could have tar -c -f - -T - with the output being written to standard output (-f -) and the list of files to be archived read from standard input (-T -).
If you see --, that is very different; it indicates the 'end of options' for the command; anything that follows is not an option, even if it starts with a dash -.

Resources