2

Sometimes it happens that I have a list of files or strings in the clipboard and want to paste it as an argument list in a bash shell:

Example file list (only an example):

createsnapshot.sh
directorylisting.sh
fetchfile.sh

What I want:

md5sum createsnapshot.sh directorylisting.sh fetchfile.sh

Currently, I enter the following hacky command line (the filenames are pasted from clipboard; the list can contain dozens of lines):

md5sum $(echo $(echo "
createsnapshot.sh
directorylisting.sh
fetchfile.sh
"))

This has several disadvantages:

  • it's complex
  • it doesn't look well
  • it doesn't support lines which contain spaces

What other options do I have? md5sum " doesn't work because in this case I get only one argument with a multi-line string. Similar with here-documents.

It's not always md5sum. It can also be tar or git add or du -hsc. I don't just ask for a way to get the md5 checksums of these files. Such situations occur about 2-5 times a day.

4

If the commands don't use stdin, use xargs, which reads input and translates it to arguments (note that I am using the echo command to show how xargs builds the command):

$ xargs echo md5sum
# paste text
createsnapshot.sh
directorylisting.sh
fetchfile.sh
# press Ctrl-D to signify end of input
md5sum createsnapshot.sh directorylisting.sh fetchfile.sh

Using xargs with -d '\n', so that each line is taken as a complete argument, spaces notwithstanding:

$ xargs -d'\n' md5sum
# paste
a file with spaces
afilewithoutspaces
foo " " bar
# Ctrl D
md5sum: a file with spaces: No such file or directory
md5sum: afilewithoutspaces: No such file or directory
md5sum: foo " " bar: No such file or directory

As you can see, md5sum prints errors for each filename, irrespective of the other whitespace in the filenames.

If you're willing to use xclip, then you can pipe or otherwise feed it to xargs:

xargs -a <(xclip -o) -d '\n' md5sum
xclip -o | xargs -d '\n' md5sum

This should reliably work with filenames with spaces.

  • @DanielAlder The first example explicitly uses echo. The second example illustrates -d '\n', that I use a file is irrelevant. – muru Nov 26 '15 at 14:08
  • omg I didn't realize what you wanted to tell me. if you just write xargs md5sum in the first line it's much clearer – Daniel Alder Nov 26 '15 at 14:09
  • @DanielAlder updated to clarify. – muru Nov 26 '15 at 14:26
5

If you don't quote a variable or a virtual variable such as a backtick invocation such as $(xclip -selection c -o) (outputs the contents of your X clipboard), the shell will split the contents on $IFS wich defaults to \t \n and , and it will expand globs present in the contents. In this case (make sure you inspect the contents of your clipboard first), that is what you want:

md5sum `xclip -selection c -o`

Note:

It's quite handy to have a shortly named wrapper around the xclip commands you need.

I use

#!/bin/sh
#filename: cb
if [ -n "$DISPLAY" ]; then
    [ -t 0 ] && exec /usr/bin/xclip -selection c -o 2>/dev/null
    /usr/bin/xclip -selection c
else
    [ -t 0 ] && exec cat "/dev/shm/$TTY_DASHED"
    cat > /dev/shm/"$TTY_DASHED"
fi

which allows me to type cb to access the clipboard and something | cb to write to it.

( I use an in-memory file named after my terminal as my clipboard if I'm outside of X (=if DISPLAY isn't set). The TTY_DASHED env variable gets set in my .profile as with export TTY_DASHED=$(tty |tr / - | tail -c+2) )

  • This is a surprising easy solution. Unfortunately, I want so see what I'm executing before pressing Enter. My doubt: There seem so be two clipboard in Linux. Sometimes Ctrl+Shift+V and Shift+Insert don't insert the same fragment... – Daniel Alder Nov 26 '15 at 13:21
  • cb(){ xclip -selection c -o; }; Run cb to see what's in the clipboard. If it's OK, run md5sum $(cb). – PSkocik Nov 26 '15 at 13:27
  • The intricacies of the X clipboard are a topic for another question. – PSkocik Nov 26 '15 at 13:28
3

You can set a variable containing your filenames. Then use the variable in your commands.

$ #VAR='<paste filenames>':
$ VAR='createsnapshot.sh
>  directorylisting.sh
> fetchfile.sh'

$ touch $VAR
$ ls -l $VAR
-rw-r--r-- 1 testuser testuser 0 nov 26 12:47 createsnapshot.sh
-rw-r--r-- 1 testuser testuser 0 nov 26 12:47 directorylisting.sh
-rw-r--r-- 1 testuser testuser 0 nov 26 12:47 fetchfile.sh

$ md5sum $VAR
d41d8cd98f00b204e9800998ecf8427e  createsnapshot.sh
d41d8cd98f00b204e9800998ecf8427e  directorylisting.sh
d41d8cd98f00b204e9800998ecf8427e  fetchfile.sh

$ tar cf archive.tar $VAR
$ tar tf archive.tar
createsnapshot.sh
directorylisting.sh
fetchfile.sh

Note: $VAR needs to be called without quotes because of the line endings. You might consider using VAR=$(echo "$VAR"|tr '\n' ' ') to be able to use quoted$VAR`

Another approach (based on the above) is to write a function in your ~/.bashrc containing:

fVAR () 
{ 
    VAR="$@";
    VAR=$(echo "$VAR"|tr '\n' ' ')
}

Call the function fVAR with the clipboard content and the values are available in $VAR:

$ fVAR '<paste filenames>'
$ md5sum "$VAR"
  • This is a very complete and helpful answer. I will test it during the next days – Daniel Alder Nov 26 '15 at 13:23
  • 1
    how is this anymore reliable than the echo the op is already using? – mikeserv Nov 26 '15 at 13:42
  • It's not more reliable but simpler. That's all I want :-) - stability requirements are lower in the interactive shell than for a script – Daniel Alder Nov 26 '15 at 13:56
0
set --
while IFS= read -r in
do    set -- "$@" "$in"
done

...then press your shift-insert keys, then ctrl+D once (or twice), and each line of your paste can afterward be literally had (sans NULs) in $1 and $2 and $3 and so on, and the entire input array can be addressed as a single string concatenated on the first character of $IFS in "$*" or else as a list of separate strings in "$@", which means you can do:

md5sum -- "$@"

...since you are apparently looking for useful interactive shorthand:

alias rdcb='
    [ -t 0 ] && set -- &&
    while IFS= read -r in
    do    set -- "$@" "$in"
    done'

...will define an alias you can run in an interactive shell to set the shell's parameters to a newline delimited array from standard input.

so...

{   ls -1 | xsel -bi                 #pipes ls output into my clipboard
    rdcb                             #terminal hangs, shift+insert && crtl+d
    xsel -bo                         #writes my clipboard to stdout
    printf %s\\n '' --- '' "$@"      #writes \n-delimited shell arg array
    printf %s\\n '' --- '' "$3" "$5" #writes only args "$3" and "$5"
}

1
edit.sh
paccache
sh
systemd-private-75115542eb544b09bc245e8ca7765854-systemd-timesyncd.service-CKruFk
sztally
yaourt-tmp-mikeserv

---

1
edit.sh
paccache
sh
systemd-private-75115542eb544b09bc245e8ca7765854-systemd-timesyncd.service-CKruFk
sztally
yaourt-tmp-mikeserv

---

paccache
systemd-private-75115542eb544b09bc245e8ca7765854-systemd-timesyncd.service-CKruFk

Here is a better version:

alias rdcb='
    if    [ -t 0 ]
    then  set -f -- "$IFS" "${IFS+IFS=\$1;} set +f -$-
          stty $(stty -g; stty raw -echo min 1 time 2)"
          IFS=$(printf \\r)
          set -- "$@" $(dd bs=4k count=1 2>/dev/null)
          eval "unset IFS;$2;shift 2"
    fi '

It drops the terminal echo and terminal special character processing and doesn't require a CTRL+D to signal end of input. All the stuff it does is temporary, and it is careful to restore all of the settings it affects to the state in which it found them when it is through, except for the shell's arguments, which are permanently altered, by design, of course.

So with this you just do rdcb, then paste, and you're done. There won't be any output to the screen when you paste though - the terminal will just hang, and then you enter at least a single byte within a decisecond and the prompt will reappear. After that you can work with the arguments as before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.