Home > other >  Appending commands to a text file breaks the script at a certain point in Linux
Appending commands to a text file breaks the script at a certain point in Linux

Time:12-11

Learning the Linux system and using unbuntu,and attempting to append a script to a .txt file in the home directory, using the |& tee -a option. The file appends fine and the script runs up until line 6, where it results in a "ls: cannot access "hos*': No such file or directory" & line 8 with the message "wc: hostname: No such file or directory".

#!/bin/bash
#Intro Script
cd /etc |& tee -a txt.
head -n 2 /etc/login.defs |& tee -a txt.
tail -n 2 /etc/login.defs |& tee -a txt.
ls -lh hos* |& tee -a txt.
hostname |& tee -a txt.
wc -l hostname |& tee -a txt.

CodePudding user response:

Pipe spawns subshells. cd /etc is executed inside a subshell as part of the pipeline, so it has no effect on current execution environment. In other words:

$ pwd
/some/dir
$ cd /anything | anything
$ pwd
/some/dir   # same as above

To affect the current execution environment, you have to execute built-in commands in the current shell, not in a subshell. cd does not output anything, so you can just remove the pipe anyway.

Do not use |&. Prefer 2>&1 |. See https://wiki.bash-hackers.org/scripting/obsolete

See http://mywiki.wooledge.org/BashFAQ/024

CodePudding user response:

As KamilCuk said, using cd in a pipeline runs it in a subshell, and therefore doesn't affect the rest of the script. It looks like you want to redirect all output and errors from the script to the txt. file (is it really supposed to have a . at the end?); if so, just add this at the beginning of the script:

exec > >(tee -a txt.) 2>&1

This redirects output & errors from the shell process to a (backgrounded) tee process. Note that the >( ) construct (a "process substitution") is only available in bash, not most other shells (or even bash when run under the name "sh").

If you only want to redirect output for part of the script, you can enclose that part in { } and redirect output from that block:

{
    cd /etc
    ...
    wc -l hostname
} > >(tee -a txt.) 2>&1

BTW, it's also a good idea to add an error-check to any cd command in a script, so if it fails the rest of the script doesn't just blindly keep running in the wrong place. Something like this:

cd /etc || {
    echo "Error in cd command, exiting..." >&2
    exit 1
}

But beware that if this is executing in a subshell for any reason, that'll only exit the subshell.

  • Related