Home > Software design >  How to get/parse the "target" of a redirect in (ba)sh command
How to get/parse the "target" of a redirect in (ba)sh command

Time:10-19

I have to run a binary through a simulator, but the binary is part of a huge and complicated benchmarking suite. So what on the normal system would look like this:

# First form
./binary -long=list -of=args <input.txt >std.out 2>std.err

needs to look like this:

# second form
simulator -c ./binary -o "-long=list -of=args" -i input.txt --output=std.out --errout=std.err 

The benchmarking suit is passing around the command, i.e. the binary with all the stuff after it (args and redirects) in (bash) variables e.g. $CMD="./binary -long=list -of=args <input.txt >std.out 2>std.err", and I would need to find out what are the targets of all the redirects (i.e. intpu.txt, std.out and std.err in the example) so I can convert the command in the first form to the command in the second form).

So is there a way to, for example, tell bash to take the command $CMD, which contains the binary, the args and the redirects, and parse it, and then return, say, what was the input redirect (i.e. input.txt), or one of the other redirects (std.out or std.err)?

Without manually doing the parsing of course...

Something like this:

$ CMD="./binary -long=list -of=args <input.txt >std.out 2>std.err"
$ bash -secret_option_to_parse_redirect_target=input "$CMD"
intpu.txt
$ bash -secret_option_to_parse_redirect_target=output "$CMD"
std.out
$ bash -secret_option_to_parse_redirect_target=error "$CMD"
std.err

CodePudding user response:

If you trust your cmd to parse to a single simple command, you can use eval to add a wrapper around it that reads the command line and active redirections. See this code running at https://replit.com/@CharlesDuffy2/GrouchyHelplessInformationtechnology#main.sh

Note that this requires an operating system with a /proc/*/fd interface compatible with that provided by Linux.

read_fd() {
  local fd_num dest_var fd_dest default_val bash_pid=$BASHPID
  fd_num=$1; dest_var=$2; default_val=${3:-"/proc/self/fd/$fd_num"}
  printf -v "$dest_var" %s "$default_val"
  [[ -e /proc/$bash_pid/fd/$fd_nume ]] || return
  fd_dest="$(readlink "/proc/$bash_pid/fd/$fd_num")" || return
  [[ -e $fd_dest ]] || return
  printf -v "$dest_var" %s "$fd_dest"
}

parse_cmd() {
  read_fd 0 stdin_src
  read_fd 1 stdout_dest
  read_fd 2 stderr_dest
  argv_dest=( "$@" )
}

...used as:

# all-caps variable names are reserved; do not use them in your own code
cmd="./binary -long=list -of=args <input.txt >std.out 2>std.err"

# inputs need to exist for redirections to work
touch input.txt

eval "parse_cmd $cmd"
echo "stdout destination is $stdout_dest"
echo "stderr destination is $stderr_dest"
echo "stdin source is $stdin_src"
echo "argument list follows, one per line:"
printf ' - %q\n' "${argv_dest[@]}"
echo
echo "to run this, you could use:"
echo "${argv_dest[*]@Q} <$stdin_src >$stdout_dest 2>$stderr_dest"
  • Related