NOTE: All examples presented here are to be run from the directory where files and the list of filenames are located. For instance, if they are in /mnt/Hard\ Drive/some\ files/
folder, please ensure the filenames.txt
is stored there as well. When executing the commands from shell, please cd /mnt/Hard\ Drive/some\ files/
and then run the examples.
AWK
AWK's system call can be used with items that contains spaces, but it's slightly complicated. Your original code had a few syntax errors and useless cat
there. Regardless of that fact, proper way would be for command to be build with sprintf()
into variable first, and pass that variable to system()
. Like so:
$ ls dir\ with\ spaces/
$ awk '{cmd=sprintf("fpath=$(realpath -e \"%s\" );%s %s \"$fpath\" \"%s\"",$0,"ln","-s","dir with spaces/");system(cmd) }' filenames.txt
$ ls dir\ with\ spaces/
ascii.txt@ disksinfo.txt@ input.txt@ process info.txt@
BASH ( or any Bourne-like shell )
The path of lesser resistance, would be via small shell script given below:
#!/bin/bash
# uncomment set -x for debugging
#set -x
# Preferably, the directory should be full path
dir="dir with spaces/"
while IFS= read -r line
do
if [ "x$line" != "x" ] && [ -e "$line" ];
then
fpath=$( realpath -e "$line" )
ln -s "$fpath" "$dir"
fi
done < filenames.txt
Script in action:
$ ls -l dir\ with\ spaces/
total 0
$ cat filenames.txt
input.txt
ascii.txt
disksinfo.txt
process info.txt
$ ./makelinks.sh
$ ls -l dir\ with\ spaces/
total 0
lrwxrwxrwx 1 xieerqi xieerqi 9 1月 15 00:47 ascii.txt -> ascii.txt
lrwxrwxrwx 1 xieerqi xieerqi 13 1月 15 00:47 disksinfo.txt -> disksinfo.txt
lrwxrwxrwx 1 xieerqi xieerqi 9 1月 15 00:47 input.txt -> input.txt
lrwxrwxrwx 1 xieerqi xieerqi 16 1月 15 00:47 process info.txt -> process info.txt
The way it works is simple: while IFS= read -r line; do . . . done < filenames.txt
reads filenames.txt line by line, each time line going into line
variable. We check if that line isn't empty and if the file exists ( this script is to be executed from the same directory where original files and the file-list are located ). If both conditions are true, we make links.
Note that you should ensure the file ends with a newline - if there's a last line that doesn't end with newline character ( which does happen ) , that final line will be skipped, thus there won't be any link made for that file.
Although not ideal and not without quirks, this is a working solution for cases where awk's system()
is not available ( which is probably rare nowadays ).
xargs
A slightly simpler shell approach would be via xargs
and bash -c '' sh
, where we read filenames.txt , again, line by line with -L1
flag, and use each line as arguments to bash. Using $@
we take whole array of command-line arguments ( $1,$2,$3 . . . etc.) and turn in into string variable, which is then passed on to ln
. The sh
at the end is to set $0
variable. It's arguable whether it's necessary to do, but that's not the topic of this question, hence let's skip it:)
$ xargs -I {} -L1 bash -c 'file="$@";fpath=$(realpath -e "$file"); ln -s "$fpath" ./dir\ with\ spaces/"$file" ' sh < filenames.txt
$ ls dir\ with\ spaces/
ascii.txt@ disksinfo.txt@ input.txt@ process info.txt@
Python
$ ls dir\ with\ spaces/
$ python -c "import os,sys;fl=[f.strip() for f in sys.stdin];map(lambda x: os.symlink(os.path.realpath(x),'./dir with spaces/' + x),fl)" < filenames.txt
$ ls dir\ with\ spaces/
ascii.txt@ disksinfo.txt@ input.txt@ process info.txt@
The way this works is simple - we redirect filenames.txt
into python's stdin
, read in each line into list fl
, and then run os.symlink()
for each item in list. Lengthy one-liner, but works.
Perl
A shorter version is achieved via Perl:
$ perl -lane 'use Cwd "realpath";symlink(realpath($_),"./dir with spaces/" . $_)' < filenames.txt
$ ls dir\ with\ spaces/
ascii.txt@ disksinfo.txt@ input.txt@ process info.txt@
filenames.txt
only filenames or full paths ? – Sergiy Kolodyazhnyy Jan 15 '17 at 07:30awk
for you. There was some trickery that's needed to be done viasprintf()
– Sergiy Kolodyazhnyy Jan 15 '17 at 10:18