Using Bash, read into an array with the IFS set to a tab:
$ cat data
ls ~
cd /home
cp dir1 dir2
al a b c d
$ vis -t -n data
ls^I~^J
cd^I/home^J
cp^Idir1^Idir2^J
al^Ia b^I c d ^J
$ while IFS=$'\t' read -r -a xyz; do al "${xyz[@]}"; done < data
ls
~
cd
/home
cp
dir1
dir2
al
a b
c d
$ while IFS=$'\t' read -r -a xyz; do al "${xyz[@]}"; done < data | vis -t -n
ls^J
~^J
cd^J
/home^J
cp^J
dir1^J
dir2^J
al^J
a b^J
c d ^J
$
The vis
program with options -t -n
prints tabs as ^I
and newlines as ^J
(followed by a newline); the al
program prints its arguments one per line — it is effectively equivalent to printf "%s\n" "$@"
(it's actually a very simple C program, but the result is the same).
With real commands in the file to be executed, you'd write:
while IFS=$'\t' read -r -a xyz; do "${xyz[@]}"; done < data
On my machine, I got:
$ (while IFS=$'\t' read -r -a xyz; do "${xyz[@]}"; done < data )
ls: ~: No such file or directory
cp: dir1: No such file or directory
a b
c d
$
I used a sub-shell because I didn't want to leave my current directory in my main shell, and I don't have a dir1
to be copied to dir2
. Note that the shell did not do tilde-expansion on the results of the parameter expansion, so ls
got to see an actual tilde, not the value of my home directory. Fixing the tilde expansion would be painful — excruciatingly painful (see Tilde expansion in quotes). It also means range notation such as {1..10}
would not be expanded, and that aliases would not be expanded. See Shell expansions for what would and would not occur.