Some strange output on my command in linux shell - linux

I've written the following in the command:
$ cat /bin/ls > blah
$ cat blah blah blah > bbb
$ chmod u+x bbb
$ ./bbb
And it printed all the file names in the current working directory.
My question is why? and why not 3 times?

Because the Linux executable file format (ELF) is not a script that you can copy-paste three times in a row to get the same result. To be more precise, the header contains a single entry point (think of it as the address of where int main() has been stored), which is where the instructions are read from. Once you reach the final return 0; or whatever, the program stops, even if there is more (nicely structured) binary garbage following in the binary file.
TL;DR: Don't forget - /bin/ls is a compiled binary and not a shell script.

Related

Can "tee" command in linux print both the input and the output of a C program?

I have a simple C program where the it will ask to take an integer from the user, and then it will print that integer.
#include <stdio.h>
int main() {
int number;
printf("Enter an integer: ");
scanf("%d", &number);
printf("You entered: %d", number);
return 0;}
When I use this command:
gcc program.c -o test
./test | tee text.txt
The program running on terminal does not print the enter integer line but instead, waits for an input and when I provide that input, it prints it and also into the text.txt folder. I want to run the program as it is and store everything running on terminal into the text.txt folder including both the input and the output. Any possible way to do that?
The tee command works with one input, but you want to capture two. With some care, you could use two separate tee commands two copy both the input and the output to the same file, but you would be better off with a utility designed for your purpose, such as script.
For Debian based Linuxes, run apt install devscripts, and then try the annotate-output util. For example, run cat using a process substitution and a file that's not there at all:
annotate-output cat <(echo hello) /bin/nosuchfile
...which shows what otherwise would be input, output, and standard error output, all sent to standard output which could then be piped to a file:
13:01:03 I: Started cat /dev/fd/63 /bin/nosuchfile
13:01:03 O: hello
13:01:03 E: cat: /bin/nosuchfile: No such file or directory
13:01:03 I: Finished with exitcode 1

How do you append a string built with interpolation of vars and STDIN to a file?

Can someone fix this for me.
It should copy a version log file to backup after moving to a repo directory
Then it automatically appends line given as input to the log file with some formatting.
That's it.
Assume existence of log file and test directory.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG |
VHENTRY="- **${LOGDATE}** | ${VHMSG}"
cat ${VHENTRY} >> versionlog.MD
shell output
virufac#box:~/Git/test$ ~/.logvh.sh
MSG > testing script
EOF
EOL]
EOL
e
E
CTRL + C to get out of stuck in reading lines of input
virufac#box:~/Git/test$ cat versionlog.MD
directly outputs the markdown
# Version Log
## version 0.0.1 established 01-22-2020
*Working Towards Working Mission 1 Demo in 0.1 *
- **01-22-2020** | discovered faker.Faker and deprecated old namelessgen
EOF
EOL]
EOL
e
E
I finally got it to save the damned input lines to the file instead of just echoing the command I wanted to enter on the screen and not executing it. But... why isn't it adding the lines built from the VHENTRY variable... and why doesn't it stop reading after one line sometimes and this time not. You could see I was trying to do something to tell it to stop reading the input.
After some realizing a thing I had done in the script was by accident... I tried to fix it and saw that the | at the end of the read command was seemingly the only reason the script did any of what it did save to the file in the first place.
I would have done this in python3 if I had know this script wouldn't be the simplest thing I had ever done. Now I just have to know how you do it after all the time spent on it so that I can remember never to think a shell script will save time again.
Use printf to write a string to a file. cat tries to read from a file named in the argument list. And when the argument is - it means to read from standard input until EOF. So your script is hanging because it's waiting for you to type all the input.
Don't put quotes around the path when it starts with ~, as the quotes make it a literal instead of expanding to the home directory.
Get rid of | at the end of the read line. read doesn't write anything to stdout, so there's nothing to pipe to the following command.
There isn't really any need for the VHENTRY variable, you can do that formatting in the printf argument.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG
printf -- '- **%s** | %s\n' "${LOGDATE}" "$VHMSG" >> versionlog.MD

abyss-pe: variables to assemble multiple genomes with one command

How do I rewrite the following to properly replace the variable with my genomeID? (I have it working with this method in Spades and Masurca assemblers, so it's something about Abyss that doesnt like this approach and I need a work-around)
I am trying to run abyss on a cluster server but am running into trouble with how abyss-pe is reading my variable input:
my submit file loads a script for each genome listed in a .txt file
my script writes in the genome name throughout the script
the abyss assembly fumbles the variable replacement
Input.sub:
queue genomeID from genomelisttest.txt
Input.sh:
#!/bin/bash
genomeID=$1
cp /mnt/gluster/harrow2/trim_output/${genomeID}_trim.tar.gz ./
tar -xzf ${genomeID}_trim.tar.gz
rm ${genomeID}_trim.tar.gz
for k in `seq 86 10 126`; do
mkdir k$k
abyss-pe -C k$k name=${genomeID} k=$k lib='pe1 pe2' pe1='../${genomeID}_trim/${genomeID}_L1_1.fq.gz ../${genomeID}_trim/${genomeID}_L1_2.fq.gz' pe2='../${genomeID}_trim/${genomeID}_L2_1.fq.gz ../${genomeID}_trim/${genomeID}_L2_2.fq.gz'
done
Error that I get:
`../enome_trim/enome_L1_1.fq.gz': No such file or directory
This is where "enome" is supposed to replace with a five digit genomeID, which happens properly in the earlier part of the script up to this point, where abyss comes in.
pe1='../'"$genomeID"'_trim/'"$genomeID"'_L1_1.fq.gz ...'
I added a single quote before and after the variable

Executable octave script: 'usr/local/bin/octave: invalid option -- '

I am trying to write an Octave script that I can run as an executable.
I am using octave version 3.6.0. I am running the following script downloaded form here:
#!/usr/local/bin/octave -qf
# An example Octave script
len = input( "What size array do you wish to use for the evaluation: " );
clear a;
tic();
for i=1:len
a(i) = i;
endfor
time1 = toc();
a = [1];
tic();
for i=2:len
a = [a i];
endfor
time2 = toc();
a=zeros( len, 1 );
tic();
for i=1:len
a(i) = i;
endfor
time3 = toc();
printf( "The time taken for method 1 was %.4f seconds\n", time1 );
printf( "The time taken for method 2 was %.4f seconds\n", time2 );
printf( "The time taken for method 3 was %.4f seconds\n", time3 );
However when I run the script on the command line, I get the following error:
'usr/local/bin/octave: invalid option -- '
However, when I type the same command at the command line:
/usr/local/bin/octave -qf
I get the octave command prompt. What am I doing wrong?
I assume you're on some sort of Unix/Linux system. Is the file in "DOS" format, with DOS-style line endings? This could cause problems with how the command is interpreted.
Your shebang line (which, btw, has a space it shouldn't) is calling /usr/local/bin/octave, but the error is coming from /usr/bin/octave. Is that a mistake? If so, you need to copy-and-paste code and errors for things like that. If not, the local version may be a script that calls the binary with an incorrect option when run non-interactively. In particular, it looks like the script (or something, at least) is trying to use a long option (--option), and the binary doesn't support it (so it's interpreting it as a short option).
Firstly the posted script runs fine on my system.
I type nano test.sh, I copy it to the file, I change the first line to be #!/usr/bin/octave -qf.
I press Ctrl-O and Ctrl-X.
I then make the script executable using chmod +x test.sh.
I then run the script using ./test.sh or octave -qf test.sh and it works as expected.
Notes:
Octave on my system is
$ which octave
/usr/bin/octave
$ file /usr/bin/octave
/usr/bin/octave: symbolic link to `octave-3.6.1'
$file /usr/bin/octave-3.6.1
/usr/bin/octave-3.6.1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x061b0a703928fc22af5ca93ee78346a7f5a0e481, stripped
And on my system /usr/bin and /usr/local/bin are in $PATH
The only way I can generate the error you mention is by changing the first line of the script to
#!/usr/bin/octave - - or #!/usr/bin/octave -q -f.
This gives
$ ./test.sh
/usr/bin/octave: invalid option -- ' '
This means that in the script on your machine the shebang line is incorrect or is being interpreted incorrectly.
Verify that the first line is correct in the script.
Also identify what happens if the line is changed to #!/usr/local/bin/octave -q or to #!/usr/local/bin/octave -f.
For more information on the parsing of shebang line see :
The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours.
how to use multiple arguments with a shebang (i.e. #!)?
Bug in #! processing - One More Time

Bash script execution with and without shebang in Linux and BSD

How and who determines what executes when a Bash-like script is executed as a binary without a shebang?
I guess that running a normal script with shebang is handled with binfmt_script Linux module, which checks a shebang, parses command line and runs designated script interpreter.
But what happens when someone runs a script without a shebang? I've tested the direct execv approach and found out that there's no kernel magic in there - i.e. a file like that:
$ cat target-script
echo Hello
echo "bash: $BASH_VERSION"
echo "zsh: $ZSH_VERSION"
Running compiled C program that does just an execv call yields:
$ cat test-runner.c
void main() {
if (execv("./target-script", 0) == -1)
perror();
}
$ ./test-runner
./target-script: Exec format error
However, if I do the same thing from another shell script, it runs the target script using the same shell interpreter as the original one:
$ cat test-runner.bash
#!/bin/bash
./target-script
$ ./test-runner.bash
Hello
bash: 4.1.0(1)-release
zsh:
If I do the same trick with other shells (for example, Debian's default sh - /bin/dash), it also works:
$ cat test-runner.dash
#!/bin/dash
./target-script
$ ./test-runner.dash
Hello
bash:
zsh:
Mysteriously, it doesn't quite work as expected with zsh and doesn't follow the general scheme. Looks like zsh executed /bin/sh on such files after all:
greycat#burrow-debian ~/z/test-runner $ cat test-runner.zsh
#!/bin/zsh
echo ZSH_VERSION=$ZSH_VERSION
./target-script
greycat#burrow-debian ~/z/test-runner $ ./test-runner.zsh
ZSH_VERSION=4.3.10
Hello
bash:
zsh:
Note that ZSH_VERSION in parent script worked, while ZSH_VERSION in child didn't!
How does a shell (Bash, dash) determines what gets executed when there's no shebang? I've tried to dig up that place in Bash/dash sources, but, alas, looks like I'm kind of lost in there. Can anyone shed some light on the magic that determines whether the target file without shebang should be executed as script or as a binary in Bash/dash? Or may be there is some sort of interaction with kernel / libc and then I'd welcome explanations on how does it work in Linux and FreeBSD kernels / libcs?
Since this happens in dash and dash is simpler, I looked there first.
Seems like exec.c is the place to look, and the relevant functionis are tryexec, which is called from shellexec which is called whenever the shell things a command needs to be executed. And (a simplified version of) the tryexec function is as follows:
STATIC void
tryexec(char *cmd, char **argv, char **envp)
{
char *const path_bshell = _PATH_BSHELL;
repeat:
execve(cmd, argv, envp);
if (cmd != path_bshell && errno == ENOEXEC) {
*argv-- = cmd;
*argv = cmd = path_bshell;
goto repeat;
}
}
So, it simply always replaces the command to execute with the path to itself (_PATH_BSHELL defaults to "/bin/sh") if ENOEXEC occurs. There's really no magic here.
I find that FreeBSD exhibits identical behavior in bash and in its own sh.
The way bash handles this is similar but much more complicated. If you want to look in to it further I recommend reading bash's execute_command.c and looking specifically at execute_shell_script and then shell_execve. The comments are quite descriptive.
(Looks like Sorpigal has covered it but I've already typed this up and it may be of interest.)
According to Section 3.16 of the Unix FAQ, the shell first looks at the magic number (first two bytes of the file). Some numbers indicate a binary executable; #! indicates that the rest of the line should be interpreted as a shebang. Otherwise, the shell tries to run it as a shell script.
Additionally, it seems that csh looks at the first byte, and if it's #, it'll try to run it as a csh script.

Resources