Calling a custom emacs function with arguments - shell

I've got this function in my .bash_rc:
function ForwardSearchXdvi {
latex -src *.tex;
for i in *.dvi; do xdvi -sourceposition "$1 ${i/.dvi/.tex}" $i; done ;
}
it works... I call it by command line with the $1 argument (the target line number in my file.tex) and it's fine.
I'd like to run it directly from emacs so I made this command:
(defun ForwardXdviSearch ()
(interactive)
(shell-command (format "bash -ic %s" (shell-quote-argument "latex -src J[HCI]*.tex; for i in J[HCI]*.dvi; do xdvi -sourceposition \"$1 ${i/.dvi/.tex}\" $i; done ;")))
)
How can I pass the argument $1 to the function when I call it with "M-x Function" ?

You would need to use special form interactive for reading arguments. Something like this untested code:
(defun forward-xdvi-search (line-number)
(interactive "nForward to line: ")
(shell-command
(format "bash -ic %s"
(shell-quote-argument
(format "latex -src J[HCI]\*.tex; for i in J[HCI]\*.dvi; do xdvi -sourceposition \"%d ${i/.dvi/.tex}\" $i; done ;"
line-number)))))
Edited with the improvement suggested by #phils

Related

Difference when executing bash function in an alias

I have a function in my .bash_profile for printing text some pre-written text and copying it to the clipboard.
copyandprint () {
s='\\033[1;32m' #strong
n='\\033[0m' #normal
printf -- "printf -- '$1' | pbcopy;" #pbcopy copies to clipboard in macOS
printf -- "echo -e copied '${s}$1${n}' to clipboard"
}
I use this to alias things I keep wanting to paste into other applications, like static IDs, or just silly things that are difficult to type quickly on a keyboard.
alias shrug=$( copyandprint '¯\_(ツ)_/¯')
But when I wanted to use it with text generated at the time I use the alias, I can't just call it in the alias definition; the alias needs to call it.
alias copydate=$( copyandprint "$(date)" )
The value is generated when the script is run, not when the alias is used.
Through pretty much sheer trial and error, I was able to make a modified version of the function that does what I wanted:
copyandprint_live () {
s='\\033[1;32m' #strong
n='\\033[0m' #normal
printf -- "$1" | pbcopy
printf -- "echo -e copied ${s}$1${n} to clipboard"
}
alias copydate_live="\$( copyandprint_live \"\$(date)\" )"
The date is generated at the time the alias is used, rather than at the time the script is executed.
But when I use that function the way I used the other one, it fails:
alias shrug_2=$( copyandprint_live '¯\_(ツ)_/¯')
$ shrug_2
#=> -bash: syntax error near unexpected token `ツ'
And I tried putting double quotes, but that didn't work
alias shrug_3=$( copyandprint_live '"¯\_(ツ)_/¯"')
$ shrug_3
#=> copied 033[1
#=> -bash: 32m¯\_(ツ)_/¯033[0m: No such file or directory
My question is, what's going on here? Why do they need to be so different?
Dispensing with the aliases and using functions makes this a lot easier.
copyandprint () {
printf '%s' "$1" | pbcopy
printf 'copied \033[1;32m%s\033[0m to clipboard\n' "$1"
}
shrug () {
copyandprint '¯\_(ツ)_/¯'
}
copydate () {
copyandprint "$(date)"
}
Functions work alike any other command:
$ foo () { echo hi; }
$ foo
hi
You're calling the function when you define the aliases, not when you use them. You need to put the alias definition in single quotes to prevent $(...) from executing the command at that time.
alias shrug='$( copyandprint "¯\_(ツ)_/¯")'

eval cat inside a function

I've been trying to evaulate an expression inside a function as follows:
eval "fn() { $(cat fn.sh); }"
Where fn.sh contains the following:
#!/bin/sh
echo "You provided $1."
So that when I call:
fn "a phrase"`
it prints "You provided a phrase.". However I cannot get it to work.
What's particularly frustrating is that:
eval "$(cat fn.sh)"
works perfectly! What am I missing here?
What I've tried:
eval "fn() { \"\$(cat fn.sh)\"; }"
fn
# bash: #!/bin/sh
# echo "You provided $1."
# return 1: No such file or directory
eval "fn() { \$(cat fn.sh); }"
fn
# bash: #!/bin/sh: No such file or directory
and myriad other combinations, most of which at this point is guess work.
Found the answer:
eval "fn() { eval \"\$(cat "fn.sh")\"; }"
Mandatory reference that explains to the best degree I understand the security risks of using eval.
Just use source/. from inside the new function.
fn () {
. fn.sh "$1"
}
If the function is used often enough where you think repeated disk I/O would be an issue, the file will almost certainly be in a disk cache when you call fn.

Extract text from gimp xcf files

I can't get out from lisp, to pass to bash, in the same script...
#!/bin/bash
{
gimp -n -i -b - <<EOF
(let* ( (file's (cadr (file-glob "*.xcf" 1))) (filename "") (image 0) (number 0) (condition 0) (testo "") )
(while (pair? file's)
...
(gimp-quit 0)
)
EOF
}
echo $testo;
The value of testo in your gimp code will not be reflected in your shell's environment. To do that, you would need to print the value and capture the output.
The general way of doing that in shell is var=$(command) (this sets the value of var to the standard output from command).

Zsh autocomplete function based on 2 arguments

I have a function like this:
p() { cd ~/Clients/$1/Projects/$2; }
Then I can type:
p "Client here" "Project here"
and it takes me to:
~/Clients/Client here/Projects/Project here
Nothing special going on here. But how do I implement autocomplete for this function? I managed to get autocompletion work for the first argument (clients):
_p() { _files -W ~/Clients -/; }
compdef _p p
But how do I autocomplete the second argument (projects)? It needs to be autocompleted from the folder based on the client:
~/Clients/$1/Projects
Hope somebody can help! :-)
A clever person (Mikachu) on IRC helped out:
p() { cd ~/Clients/$1/Projects/$2; }
_p() {
_arguments '1: :->client' '2: :->project'
case $state in
client)
_files -W ~/Clients
;;
project)
_files -W ~/Clients/$words[CURRENT-1]/Projects
;;
esac
}
compdef _p p
UPDATE: Change $words[CURRENT-1] to ${(Q)words[CURRENT-1]} to make it work with directories containing spaces:
p() { cd ~/Clients/$1/Projects/$2; }
_p() {
_arguments '1: :->client' '2: :->project'
case $state in
client)
_files -W ~/Clients
;;
project)
_files -W ~/Clients/${(Q)words[CURRENT-1]}/Projects
;;
esac
}
compdef _p p

Multiple shell parameter assignments prefixing a bash command

In the bash manual section 3.7.4, it states
The environment for any simple command or function may be augmented
temporarily by prefixing it with parameter assignments, as described
in Shell Parameters [section 3.4].
And a trivial example of this is
MYVAR=MYVALUE mycommand
I have read section 3.4 and I still can't figure out how to specify multiple parameter assignments. Note that the statement in 3.7.4 is definitely plural, implying that it is possible.
The following does not seem to work:
MYVAR1=abc MYVAR2=xyz mycommand
I am using bash version 4.1, 23 December 2009.
It should work. That is acceptable syntax. Here's an example:
$ cat a
#!/bin/sh
echo $MYVAR1
echo $MYVAR2
$ ./a
$ MYVAR1=abc MYVAR2=xyz ./a
abc
xyz
$
UPDATE: Your updated example given in your answer will work if you precede the simple command with the variables as required:
mycommand () { echo MYVAR1=[$MYVAR1]; echo MYVAR2=[$MYVAR2]; }
for f in ~/*.txt ; do MYVAR1=abc MYVAR2=xyz mycommand; done
Oops, my example was over-simplified. This works fine:
mycommand () { echo MYVAR1=[$MYVAR1]; echo MYVAR2=[$MYVAR2]; }
MYVAR1=abc MYVAR2=xyz mycommand
but this does not:
mycommand () { echo MYVAR1=[$MYVAR1]; echo MYVAR2=[$MYVAR2]; }
MYVAR1=abc MYVAR2=xyz for f in ~/*.txt; do mycommand; done
and the key difference is this phrase:
for any simple command or function
A for command is a complex command, not "a simple command or function", according to section 3.2.
I've never seen that method used.
You could always just pass in regular parameters to the script.
Updating for new example:
This works in both situations:
> mycommand () { echo MYVAR1=[$1]; echo MYVAR2=[$2]; }
> mycommand a b
MYVAR1=[a]
MYVAR2=[b]
> for f in ~/file*.txt; do mycommand a b; done
MYVAR1=[a]
MYVAR2=[b]
MYVAR1=[a]
MYVAR2=[b]

Resources