I would like a quick keyboard command sequence to copy the current command at a bash prompt to the clipboard.
So that, for example, to copy the last bash command to the clipboard, I'd press up+[some command sequence] to copy it. Or, for example, to search for a command in bash hisory, I'd use ctrl+r, search, display it on the command prompt, and then [some command sequence] to copy it, etc.
My current solution is using bash pipes: Pipe to/from the clipboard
So, to copy the previous command to clipboard:
echo "!!" | pbcopy
Which isn't too terrible, but what if the command to copy isn't the last command, etc.
What's the proper way to achieve what I'm trying to achieve here?
Taking #Lauri's post for inspiration, here's a solution using the bind command:
bind '"\C-]":"\C-e\C-u pbcopy <<"EOF"\n\C-y\nEOF\n"'
ctrl-] then will copy whatever is on the current bash prompt to the clipboard.
To make it persistent, you can add the bind command as above to your ~/.bashrc, or you can strip off the outer quotes and remove the 'bind' part of the call and add the result to your ~/.inputrc.
Non-OS-X users will have to swap pbcopy out with the appropriate command, probably xclip.
A quoted heredoc was used instead of a an echo+pipe technique so that both single and double quotes in the command at the bash prompt are preserved. With this technique, for example, I was able to hit ctrl-], copy the actual bind command from the terminal prompt, and paste it here in the answer. So the heredoc technique handles all of the special characters in the bind command here.
You can use READLINE_LINE with bind -x in bash 4:
copyline() { printf %s "$READLINE_LINE"|pbcopy; }
bind -x '"\C-xc":copyline'
You can install bash 4 and make it the default login shell by running brew install bash;echo /usr/local/bin/bash|sudo tee -a /etc/shells;chsh -s /usr/local/bin/bash.
I also use this function to copy the last command:
cl() { history -p '!!'|tr -d \\n|pbcopy; }
I spent a decent amount of time today writing a simple zsh implementation for macOS; usage is as follows:
example command: git commit -m "Changed a few things"
command that copies: c git commit -m "Changed a few things"
# The second command does not actually execute the command, it just copies it.
# Using zsh, this should reduce the whole process to about 3 keystrokes:
#
# 1) CTRL + A (to go to the beginning of the line)
# 2) 'c' + ' '
# 3) ENTER
preexec() is a zsh hook function that gets called right when you press enter, but before the command actually executes.
Since zsh strips arguments of certain characters like ' " ', we will want to use preexec(), which allows you to access the unprocessed, original command.
Pseudocode goes like this:
1) Make sure the command has 'c ' in the beginning
2) If it does, copy the whole command, char by char, to a temp variable
3) Pipe the temp variable into pbcopy, macOS's copy buffer
Real code:
c() {} # you'll want this so that you don't get a command unrecognized error
preexec() {
tmp="";
if [ "${1:0:1}" = "c" ] && [ "${1:1:1}" = " " ] && [ "${1:2:1}" != " " ]; then
for (( i=2; i<${#1}; i++ )); do
tmp="${tmp}${1:$i:1}";
done
echo "$tmp" | pbcopy;
fi
}
Go ahead and stick the two aforementioned functions in your .zshrc file, or wherever you want (I put mine in a file in my .oh-my-zsh/custom directory).
If anyone has a more elegant solution, plz speak up.
Anything to avoid using the mouse.
If xsel is installed on your system you can add this in .inputrc :
C-]: '\C-e\C-ucat <<"EOF" | tr -d "\\n" | xsel -ib\n\C-y\nEOF\n'
Alternatively, if xclip is installed you could add this:
C-]: '\C-e\C-ucat <<"EOF" | tr -d "\\n" | xclip -se c\n\C-y\nEOF\n'
Notice: Used code from #Clayton's answer.
I use history to find the command number that I am looking for, then I do:
echo "!command_number" | xclip -in
$ history | cut -c 8- | tail -1 | pbcopy
or in .zshrc file add an alias
alias copy='history | cut -c 8- | tail -1 | pbcopy'
Related
Take the following example:
ls -l | grep -i readme | ./myscript.sh
What I am trying to do is get ls -l | grep -i readme as a string variable in myscript.sh. So essentially I am trying to get the whole command before the last pipe to use inside myscript.sh.
Is this possible?
No, it's not possible.
At the OS level, pipelines are implemented with the mkfifo(), dup2(), fork() and execve() syscalls. This doesn't provide a way to tell a program what the commands connected to its stdin are. Indeed, there's not guaranteed to be a string representing a pipeline of programs being used to generate stdin at all, even if your stdin really is a FIFO connected to another program's stdout; it could be that that pipeline was generated by programs calling execve() and friends directly.
The best available workaround is to invert your process flow.
It's not what you asked for, but it's what you can get.
#!/usr/bin/env bash
printf -v cmd_str '%q ' "$#" # generate a shell command representing our arguments
while IFS= read -r line; do
printf 'Output from %s: %s\n' "$cmd_str" "$line"
done < <("$#") # actually run those arguments as a command, and read from it
...and then have your script start the things it reads input from, rather than receiving them on stdin.
...thereafter, ./yourscript ls -l, or ./yourscript sh -c 'ls -l | grep -i readme'. (Of course, never use this except as an example; see ParsingLs).
It can't be done generally, but using the history command in bash it can maybe sort of be done, provided certain conditions are met:
history has to be turned on.
Only one shell has been running, or accepting new commands, (or failing that, running myscript.sh), since the start of myscript.sh.
Since command lines with leading spaces are, by default, not saved to the history, the invoking command for myscript.sh must have no leading spaces; or that default must be changed -- see Get bash history to remember only the commands run with space prefixed.
The invoking command needs to end with a &, because without it the new command line wouldn't be added to the history until after myscript.sh was completed.
The script needs to be a bash script, (it won't work with /bin/dash), and the calling shell needs a little prep work. Sometime before the script is run first do:
shopt -s histappend
PROMPT_COMMAND="history -a; history -n"
...this makes the bash history heritable. (Code swiped from unutbu's answer to a related question.)
Then myscript.sh might go:
#!/bin/bash
history -w
printf 'calling command was: %s\n' \
"$(history | rev |
grep "$0" ~/.bash_history | tail -1)"
Test run:
echo googa | ./myscript.sh &
Output, (minus the "&" associated cruft):
calling command was: echo googa | ./myscript.sh &
The cruft can be halved by changing "&" to "& fg", but the resulting output won't include the "fg" suffix.
I think you should pass it as one string parameter like this
./myscript.sh "$(ls -l | grep -i readme)"
I think that it is possible, have a look at this example:
#!/bin/bash
result=""
while read line; do
result=$result"${line}"
done
echo $result
Now run this script using a pipe, for example:
ls -l /etc | ./script.sh
I hope that will be helpful for you :)
After the question In a shell script: echo shell commands as they are executed I wonder how can I redirect the command executed/echoed to a file (or a variable)?
I tried the usual stdout redirection, like ls $HOME > foo.txt, after setting the bash verbose mode, set -v, but only the output of ls was redirected.
PS: What I want is to have a function (call it "save_succ_cmdline()") that I could put in front of a (complex) command-line (e.g, save_succ_cmdline grep -m1 "model name" /proc/cpuinfo | sed 's/.*://' | cut -d" " -f -3) so that this function will save the given command-line if it succeeds.
Notice that the grep -m1 "model name" ... example above is just to give an example of a command-line with special characters (|,',"). What I expect from such a function "save_succ_cmdline()" is that the actual command (after the function name, grep -m1 "model name"...) is executed and the function verifies the exit code ([$? == 0]) to decide if the command-line can be save or not. If the actual command has succeeded, the function ("save_succ_cmdline") can save the command-line expression (with the pipes and everything else).
My will is to use the bash -o verbose feature to have and (temporarily) save the command-line. But I am not being able to do it.
Thanks in advance.
Your save_succ_cmdline function will only see the grep -m1 "model name" /proc/cpuinfo part of the command line as the shell will see the pipe itself.
That being said if you just want the grep part then this will do what you want.
save_succ_cmdline() {
"$#" && cmd="$#"
}
If you want the whole pipeline then you would need to quote the entire argument to save_succ_cmdline and use eval on "$#" (or similar) and I'm not sure you could make that work for arbitrary quoting.
I'm trying to alias _! to sudo the last command, but I'm running into roadblocks. !! doesn't seem to work in my .zshrc file, and sed has given me repeated problems. I tried using the following command, and several variations of it, but to no avail.
history | tail -1 | sed -e 's/[^0-9\*\ ]+/\0/g'
However, this still interpreted the piped input as a file, instead of a string of text. I also tried a variation using awk:
history | tail -1 | awk '{ gsub("/[^0-9\*\ ]+", "") ; system( "echo" $1 ) }'
I'm sure I'm just having some trouble putting the commands in correctly, but some help would be appreciated.
You can use the fc built-in to access the history programmatically. For example, I believe this will behave as you wish:
alias _!='fc -e "sed -i -e \"s/^/sudo /\""'
With no arguments, fc (short for "fix command") fires up $EDITOR on your previous command and runs the result of your editing. You can specify a different editor with the -e option; here, I'm specifying a non-interactive one in the form of a sed command that will insert sudo in front of the command line.
The command assumes GNU sed. As written, it will also work with the version that ships on modern BSD/macOS, but by way of a hackcident: it treats the -e as an argument to -i instead of a new option. Since the -e is optional with only one expression, this works fine, but it means that sed makes a backup of the temp file with -e on the end, which will hang around after the command completes. You can make that cleaner by using this alternative version on those systems:
alias _!='fc -e "sed -i \"\" -e \"s/^/sudo /\""'
(That won't work with GNU sed, which sees the empty string argument as a filename to operate on...)
On older systems, a more portable solution could use ed:
alias _!="fc -e 'ed -s <<<$'\''s/^/sudo /\nw\nq'\'"
You can often get away with something simpler, like sudo $(fc -ln -1) (-l = list commands, -n = without numbers, -1 = only the last command), but in general you will run into quoting issues, since the command is output by fc the way it was typed:
% touch '/etc/foo bar'
touch: /etc/foo bar: Permission denied
% sudo $(fc -ln -1)
touch: '/etc/foo: No such file or directory
None of the above is limited to zsh, btw; fc dates to the original version of ksh, so it will also work in bash, etc.
This fc command will always give most recently executed command in zsh and in bash:
fc -ln -1
As per help fc:
-l (letter el) list lines instead of editing
-n omit line numbers when listing
-1 (minus one) gets the just executed command.
Found a amazing widget to sudo:
sudo-command-line() {
[[ -z $BUFFER ]] && zle up-history
[[ $BUFFER != sudo\ * ]] && {
typeset -a bufs
bufs=(${(z)BUFFER})
if (( $+aliases[$bufs[1]] )); then
bufs[1]=$aliases[$bufs[1]]
fi
bufs=(sudo $bufs)
BUFFER=$bufs
}
zle end-of-line
}
zle -N sudo-command-line
bindkey "\e\e" sudo-command-line
Author:lilydjwg
The following is the way to run the last command in command:
fc -ln -1 is the simplest way, but one problem, when run something with some spaces at the beginning of the command, this command won't shown up in history, anything based on history won't work properly.
So we need ZLE(Zsh Line Editor) to store the command manually.
Store_Your_Command () {
if [[ -z $BUFFER ]]
then
# If nothing input, just clear the screen
zle clear-screen
else
zle accept-line
# Remember the last command, useful in some alias
# Add space at the beginning of a command, this command wont
# show up in history, so use variables to store the command
LAST_COMMAND=$CURRENT_COMMAND
CURRENT_COMMAND=$BUFFER
fi
}
# Create a user-defined widget
zle -N Store_Your_Command
# Bind it to the **Enter** key
bindkey "^M" Store_Your_Command
Then whenever we press enter to run a command, this command will be stored in $CURRENT_COMMAND, and the last command will be stored in $LAST_COMMAND.
Want to run the last command? Just run eval $LAST_COMMAND, you can also put it to your alias.
When some alias in the last command, zsh wont run the last command correctly, so we need to expand our alias: when we input an alias, replace the alias to the original command/content, with help of the builtin zle: _expand_alias.
First, delete the widget we just added.
Add those to your .zshrc:
# When input space, expand alias -----------------------------------{{{
expand_alias_space () {
zle _expand_alias
zle self-insert
}
zle -N expand_alias_space
bindkey " " expand_alias_space
# }}}
# When input enter, expand alias -----------------------------------{{{
expand_alias_enter () {
if [[ -z $BUFFER ]]
then
zle clear-screen
else
zle _expand_alias
zle accept-line
# Remember the last command, useful in some alias
# Add space at the beginning of a command, this command won't
# show up in history, so use variables to store the command
LAST_COMMAND=$CURRENT_COMMAND
CURRENT_COMMAND=$BUFFER
fi
}
zle -N expand_alias_enter
bindkey "^M" expand_alias_enter
# }}}
Now we can expand alias to the original command/content by press Space key or just press Enter key to run the command, and use eval $LAST_COMMAND to run the last command without any problems.
But it will call another problem when run a command use eval $LAST_COMMAND twice:
zsh: job table full or recursion limit exceeded
We need to replace eval $LAST_COMMAND to the real command, because $LAST_COMMAND always change.
We write a function run the last command like this
# Echo the last command
fun()
{
# The command we need to run in this function
CURRENT_COMMAND="echo \[`echo $LAST_COMMAND`\]"
# run the command
eval $CURRENT_COMMAND
}
The command stored in $CURRENT_COMMAND wont change like eval $LAST_COMMAND does.
problem sloved.
No more problem I hope
If I want . to be the alias for last command,
lastcmd() {
# start climbing back in history, checking for alias
n=-1
lc=$(fc -ln $n $n)
# "." is checked because I have aliased lastcmd to "."
while [ "$lc" = "lastcmd" ] || [ "$lc" = "." ]
do
n=$(( $n - 1 ))
lc=$(fc -ln $n $n)
done
eval ${lc}
}
alias .=lastcmd
This is pretty simple and works well for me. Replace the second condition in while with whatever alias you end up using.
Add this to your .zshrc --
func preexec() {
export LAST_COMMAND="$1"
}
Is the output of a Bash command stored in any register? E.g. something similar to $? capturing the output instead of the exit status.
I could assign the output to a variable with:
output=$(command)
but that's more typing...
You can use $(!!)
to recompute (not re-use) the output of the last command.
The !! on its own executes the last command.
$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre
The answer is no. Bash doesn't allocate any output to any parameter or any block on its memory. Also, you are only allowed to access Bash by its allowed interface operations. Bash's private data is not accessible unless you hack it.
Very Simple Solution
One that I've used for years.
Script (add to your .bashrc or .bash_profile)
# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out; }
# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out; }
Usage
$ find . -name 'filename' | cap
/path/to/filename
$ ret
/path/to/filename
I tend to add | cap to the end of all of my commands. This way when I find I want to do text processing on the output of a slow running command I can always retrieve it with ret.
If you are on mac, and don't mind storing your output in the clipboard instead of writing to a variable, you can use pbcopy and pbpaste as a workaround.
For example, instead of doing this to find a file and diff its contents with another file:
$ find app -name 'one.php'
/var/bar/app/one.php
$ diff /var/bar/app/one.php /var/bar/two.php
You could do this:
$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php
The string /var/bar/app/one.php is in the clipboard when you run the first command.
By the way, pb in pbcopy and pbpaste stand for pasteboard, a synonym for clipboard.
One way of doing that is by using trap DEBUG:
f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG
Now most recently executed command's stdout and stderr will be available in /tmp/out.log
Only downside is that it will execute a command twice: once to redirect output and error to /tmp/out.log and once normally. Probably there is some way to prevent this behavior as well.
Inspired by anubhava's answer, which I think is not actually acceptable as it runs each command twice.
save_output() {
exec 1>&3
{ [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
exec > >(tee /tmp/current)
}
exec 3>&1
trap save_output DEBUG
This way the output of last command is in /tmp/last and the command is not called twice.
Yeah, why type extra lines each time; agreed.
You can redirect the returned from a command to input by pipeline, but redirecting printed output to input (1>&0) is nope, at least not for multiple line outputs.
Also you won't want to write a function again and again in each file for the same. So let's try something else.
A simple workaround would be to use printf function to store values in a variable.
printf -v myoutput "`cmd`"
such as
printf -v var "`echo ok;
echo fine;
echo thankyou`"
echo "$var" # don't forget the backquotes and quotes in either command.
Another customizable general solution (I myself use) for running the desired command only once and getting multi-line printed output of the command in an array variable line-by-line.
If you are not exporting the files anywhere and intend to use it locally only, you can have Terminal set-up the function declaration. You have to add the function in ~/.bashrc file or in ~/.profile file. In second case, you need to enable Run command as login shell from Edit>Preferences>yourProfile>Command.
Make a simple function, say:
get_prev() # preferably pass the commands in quotes. Single commands might still work without.
{
# option 1: create an executable with the command(s) and run it
#echo $* > /tmp/exe
#bash /tmp/exe > /tmp/out
# option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions.
#echo `"$*"` > /tmp/out
# option 3: (I actually used below)
eval "$*" > /tmp/out # or simply "$*" > /tmp/out
# return the command(s) outputs line by line
IFS=$(echo -en "\n\b")
arr=()
exec 3</tmp/out
while read -u 3 -r line
do
arr+=($line)
echo $line
done
exec 3<&-
}
So what we did in option 1 was print the whole command to a temporary file /tmp/exe and run it and save the output to another file /tmp/out and then read the contents of the /tmp/out file line-by-line to an array.
Similar in options 2 and 3, except that the commands were exectuted as such, without writing to an executable to be run.
In main script:
#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
Now, bash saves the variable even outside previous scope, so the arr variable created in get_prev function is accessible even outside the function in the main script:
#get previous command outputs in arr
for((i=0; i<${#arr[#]}; i++))
do
echo ${arr[i]}
done
#if you're sure that your output won't have escape sequences you bother about, you may simply print the array
printf "${arr[*]}\n"
Edit:
I use the following code in my implementation:
get_prev()
{
usage()
{
echo "Usage: alphabet [ -h | --help ]
[ -s | --sep SEP ]
[ -v | --var VAR ] \"command\""
}
ARGS=$(getopt -a -n alphabet -o hs:v: --long help,sep:,var: -- "$#")
if [ $? -ne 0 ]; then usage; return 2; fi
eval set -- $ARGS
local var="arr"
IFS=$(echo -en '\n\b')
for arg in $*
do
case $arg in
-h|--help)
usage
echo " -h, --help : opens this help"
echo " -s, --sep : specify the separator, newline by default"
echo " -v, --var : variable name to put result into, arr by default"
echo " command : command to execute. Enclose in quotes if multiple lines or pipelines are used."
shift
return 0
;;
-s|--sep)
shift
IFS=$(echo -en $1)
shift
;;
-v|--var)
shift
var=$1
shift
;;
-|--)
shift
;;
*)
cmd=$option
;;
esac
done
if [ ${#} -eq 0 ]; then usage; return 1; fi
ERROR=$( { eval "$*" > /tmp/out; } 2>&1 )
if [ $ERROR ]; then echo $ERROR; return 1; fi
local a=()
exec 3</tmp/out
while read -u 3 -r line
do
a+=($line)
done
exec 3<&-
eval $var=\(\${a[#]}\)
print_arr $var # comment this to suppress output
}
print()
{
eval echo \${$1[#]}
}
print_arr()
{
eval printf "%s\\\n" "\${$1[#]}"
}
Ive been using this to print space-separated outputs of multiple/pipelined/both commands as line separated:
get_prev -s " " -v myarr "cmd1 | cmd2; cmd3 | cmd4"
For example:
get_prev -s ' ' -v myarr whereis python # or "whereis python"
# can also be achieved (in this case) by
whereis python | tr ' ' '\n'
Now tr command is useful at other places as well, such as
echo $PATH | tr ':' '\n'
But for multiple/piped commands... you know now. :)
-Himanshu
Like konsolebox said, you'd have to hack into bash itself. Here is a quite good example on how one might achieve this. The stderred repository (actually meant for coloring stdout) gives instructions on how to build it.
I gave it a try: Defining some new file descriptor inside .bashrc like
exec 41>/tmp/my_console_log
(number is arbitrary) and modify stderred.c accordingly so that content also gets written to fd 41. It kind of worked, but contains loads of NUL bytes, weird formattings and is basically binary data, not readable. Maybe someone with good understandings of C could try that out.
If so, everything needed to get the last printed line is tail -n 1 [logfile].
Not sure exactly what you're needing this for, so this answer may not be relevant. You can always save the output of a command: netstat >> output.txt, but I don't think that's what you're looking for.
There are of course programming options though; you could simply get a program to read the text file above after that command is run and associate it with a variable, and in Ruby, my language of choice, you can create a variable out of command output using 'backticks':
output = `ls` #(this is a comment) create variable out of command
if output.include? "Downloads" #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end
Stick this in a .rb file and run it: ruby file.rb and it will create a variable out of the command and allow you to manipulate it.
If you don't want to recompute the previous command you can create a macro that scans the current terminal buffer, tries to guess the -supposed- output of the last command, copies it to the clipboard and finally types it to the terminal.
It can be used for simple commands that return a single line of output (tested on Ubuntu 18.04 with gnome-terminal).
Install the following tools: xdootool, xclip , ruby
In gnome-terminal go to Preferences -> Shortcuts -> Select all and set it to Ctrl+shift+a.
Create the following ruby script:
cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}#" }
h = g[0]
print h
EOF
In the keyboard settings add the following keyboard shortcut:
bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '
The above shortcut:
copies the current terminal buffer to the clipboard
extracts the output of the last command (only one line)
types it into the current terminal
I have an idea that I don't have time to try to implement immediately.
But what if you do something like the following:
$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!
There might be issues with IO buffering. Also the file might get too huge. One would have to come up with a solution to these problems.
I think using script command might help. Something like,
script -c bash -qf fifo_pid
Using bash features to set after parsing.
Demo for non-interactive commands only: http://asciinema.org/a/395092
For also supporting interactive commands, you'd have to hack the script binary from util-linux to ignore any screen-redrawing console codes, and run it from bashrc to save your login session's output to a file.
You can use -exec to run a command on the output of a command. So it will be a reuse of the output as an example given with a find command below:
find . -name anything.out -exec rm {} \;
you are saying here -> find a file called anything.out in the current folder, if found, remove it. If it is not found, the remaining after -exec will be skipped.
I have a script named password-for-object which I normally run like that:
$ password-for-object example.com
sOtzC0UY1K3EDYp8a6ltfA
I.e. it does an intricate hash calculation and outputs a password that I should use when accessing an object (for example, website) named example.com. I'll just double click the whole password, it gets copied into my buffer and I'll paste it into the form.
I've also learnt a trick on how to use such a script without making my password visible:
$ password-for-object example.com | xclip
This way output of a script ends up in X's primary buffer and I can insert it right into password field in the form and it's not shown on the screen.
The only problem with this way is that password-for-object outputs a string with trailing newline and thus "xclip" always catches up an extra symbol - this newline. If I omit output of newline in password-for-object, then I'll end up with messed up string without xclip, i.e. when I'm just putting it on the stdout. I use 2 shells: zsh and bash, and I'll get the following in zsh (note the extra % sign):
$ password-for-object example.com
sOtzC0UY1K3EDYp8a6ltfA%
$
Or the following in bash (note that prompt would be started on the same line):
$ password-for-object example.com
sOtzC0UY1K3EDYp8a6ltfA$
Any ideas on how to work around this issue? Is it possible to modify the script in a way so it will detect that xclip is in the pipeline and only output newline if it isn't?
If you change password-for-object so that it doesn't output a newline, you can call it with a script like:
#!/bin/bash
password-for-object "$1"
if [ -t 1 ]
then
echo
fi
The -t condition is described in the bash manual as:
-t fd
True if file descriptor fd is open and refers to a terminal.
See the following question:
How to detect if my shell script is running through a pipe?
Give this a try:
$ password-for-object example.com | tr -d '\n' | xclip
tr -d '\n' deletes the newline