5
MUTE='&> /dev/null'
echo "text" $MUTE

Is there a way to make that work keeping the redirection inside the variable?

4 Answers 4

5

IMHO it would be more elegant to use the presence / value of the variable to conditionally close the file descriptors e.g.

$ cat myscript.sh
#!/bin/bash

if [ -n "$MUTE" ]; then
    exec &>-
fi

echo somestuff
echo someerr >&2

then

$ ./myscript.sh
somestuff
someerr

but

$ MUTE=yes ./myscript.sh
$

If you really want to toggle the redirection, you could consider creating a shell function that duplicates the file descriptor(s) before closing them, and then restores the duplicates to re-enable the original streams e.g.

#!/bin/bash

function mute {
  case "$1" in
    "on") 
      exec 3>&1-
      exec 4>&2-
    ;;
    "off") 
      exec 1>&3-
      exec 2>&4-
    ;;
    *)
  esac
}


# Demonstration: 

echo "with mute on: "
mute on
ls somefile
ls nofile

mute off
echo "with mute off: "
ls somefile
ls nofile

Result:

$ ./mute.sh
with mute on: 
with mute off: 
somefile
ls: cannot access nofile: No such file or directory
2
  • That is indeed elegant! The only problem is that it can't differentiate between the lines (outputs) that should be printed and the ones that shouldn't. But I solved it by combining your solution with the one of @kos. Too bad I can't set all these great answers as accepted solutions. Mar 16, 2016 at 22:38
  • 1
    @JulenLarrucea I have updated the answer with a suggestion of how to implement a truer 'toggle' functionality Mar 16, 2016 at 23:09
3

From Bash Reference Manual: Simple Command Expansion:

  1. The words that the parser has marked as variable assignments (those preceding the command name) and redirections are saved for later processing.
  2. The words that are not variable assignments or redirections are expanded (see Shell Expansions). If any words remain after expansion, the first word is taken to be the name of the command and the remaining words are the arguments.
  3. Redirections are performed as described above (see Redirections).

This means that the command parser first identifies all the redirections, then performs the various expansions and finally resolves the redirections it previously identified: those don't include possible redirections resulting from the expansions.

However from help eval:

Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

So using eval you could create a sort of level of indirection which will allow the command to be processed twice:

MUTE='&> /dev/null'
eval echo "text" $MUTE
$ MUTE='&> file'
$ eval echo "text" $MUTE
$ cat file
text
2

One could use a function that writes its stdin to wherever you want it.

$> MUTE(){  cat /dev/stdin > testFile.txt  ; }                                                    
$> df | MUTE                                                                                      
$> cat testFile.txt
Filesystem     1K-blocks     Used Available Use% Mounted on
udev             1954208        4   1954204   1% /dev
tmpfs             393160     3548    389612   1% /run
/dev/sda1      115247656 95511252  13859056  88% /
none                   4        0         4   0% /sys/fs/cgroup
none                5120        0      5120   0% /run/lock
none             1965792      872   1964920   1% /run/shm
none              102400      128    102272   1% /run/user
cgmfs                100        0       100   0% /run/cgmanager/fs

Or we could tell the function to execute whatever we want with redirection

$> MUTE(){  "$@" > testFile.txt  ; }                                                              
$> MUTE lsblk                                                                                     
$> cat testFile.txt                                                                               
NAME                             MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda                                8:0    0 111.8G  0 disk 
└─sda1                             8:1    0 111.8G  0 part /

Non-standard way, hackery , but it kind of works :)

1

It works this way, I don't know if it is usable for your approach:

MUTE='&> /dev/null'
bash -c "echo \"text\" $MUTE"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .