Can zsh `else` reserved keyword command be aliased and the lexem itself be repurposed as `fi` keyword command? - zsh-alias

Following ZSH: Call in-built function from zsh function that uses the same name and Run a command that is shadowed by an alias, it might be expected that a command keyword equivalent of what builtin and command are doing for their respective eponymous token category; so that
if [ -z 'love' ]; then echo 'sad world'; keyword else echo 'wonderful world'; fi
would be equivalent to
if [ -z 'love' ]; then echo 'sad world'; else echo 'wonderful world'; fi
This problem was found in the following tricky scenario: being able to replace else with alie and fi with else. See Can zsh buildtins be aliased? for more details.
So an hypothetical attempt to implement that, if the keyword command existed, would be:
alias alie="keyword else"
alias else='fi'
So, to sum it up, the question is how do you make the following peace of zsh code works as expected by the previous command:
if [ -z 'love' ]; then echo 'sad world'; alie echo 'wonderful world'; else

This is not yet a working solution, but here is an idea: using the -r flag of enable and disable builtin commands to change visibility of the else keyword. So:
alias se='enable -r else; if'
alias alie='else'
disable -r else
alias else="fi; disable -r else"
This unfortunately doesn't work
se [ -z 'amo' ]; then echo 'trista mondo'; alie echo 'mirinda mondo'; else
# zsh: parse error near `fi'
This is however really on the "else" substitution that something break, as a non-inline version will indeed enter the else-branch and print "mirinda mondo".

Related

Parsing arguments in fish shell without argparse

I'm using fish shell and wrote my own little parser function because I found argparse confusing. Basically, if a flag is matched, it uses the information from the following argument. However, I'm assuming my method must introduce bugs as I haven't seen this method used online. Are there advantages to using argparse that I'm missing?
function check_args
for current_arg in (seq 1 (count $argv))
#grab next argument
set next_arg $argv[(math $current_arg + 1)]
switch $argv[$current_arg]
case -h --help
usage
break
case -t --theme
echo "theme: " $next_arg
set -g theme themes/$next_arg.css
case -f --format
echo "format: " $next_arg
set -g format $next_arg
case -o --output
echo "output: " $next_arg
set -g output $next_arg
end
end
end
check_args $argv #calls the function with the passed arguments
With argparse:
# the -- is required!
argparse h/help t/theme= f/format= o/output= -- $argv
or exit 1
# just to inspect the variables
set -S _flag_h _flag_help _flag_t _flag_theme _flag_f _flag_format _flag_o _flag_output
if set -q _flag_help
usage
exit
end
set theme themes/$_flag_theme.css
set format $_flag_format
set output $_flag_output

bourne shell expr inside if-statements

Can I use the expr command inside if-statements? I am seeing very strange behaviour of the following small script:
if (`expr $1 > $2`)
then
echo $1
else
echo $2
fi
It works as intended for > and <, but gives a syntax error or "Command not found" for =, or <=, >= and pretty much everything else.
Am I doing something wrong?
I know I can use [ ] or test instead.
Yes but you don't have to use command substitution. You can just redirect the output to /dev/null and check the exit code. You should also quote special characters like redirection characters.
if expr "$1" '>' "$2" >/dev/null
then
echo "$1"
else
echo "$2"
fi

System command in perl

I need to run a system command which would go to a directory and delete sub directories excluding files if present. I wrote the below command to perform this operation:
system("cd /home/faizan/test/cache ; for i in *\; do if [ -d \"$i\" ]\; then echo \$i fi done");
The command above keeps throwing syntax error. I have tried multiple combinations but still not clear how this should go. Please suggest.
Well, your command line does contain syntax errors. Try this:
system("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
Or better yet, only loop over directories in the first place;
system("for i in /home/faizan/test/cache/*/.; do echo \$i; done");
Or better yet, do it without a loop:
system("echo /home/faizan/test/cache/*/.");
(I suppose you will want to rmdir instead of echo once it is properly debugged.)
Or better yet, do it all in Perl. There is nothing here which requires system().
You're still best off trying this as a bash command first. Formatting that properly makes it much clearer that you're missing statement terminators:
for i in *; do
if [ -d "$i" ]; then
echo $i
fi
done
And condensing that by replacing new lines with semicolons (apart from after do/then):
for i in *; do if [ -d "$i" ]; then echo $i; fi; done
Or as has been mentioned, just do it in Perl (I haven't tested this to the point of actually uncommenting remove_tree - be careful!):
use strict;
use warnings;
use File::Path 'remove_tree';
use feature 'say';
chdir '/tmp';
opendir my $cache, '.';
while (my $item = readdir($cache)) {
if ($item !~ /^\.\.?$/ && -d $item) {
say "Deleting '$item'...";
# remove_tree($item);
}
}
Using system
my #args = ("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
system(#args);
Using Subroutine
sub do_stuff {
my #args = ( "bash", "-c", shift );
system(#args);
}
do_stuff("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
As question title stand for system command, this will answer directly, but the sample command using bash contain only thing that will be simplier in perl only (take a look at other answer using opendir and -d in perl).
If you want to use system (instead of open $cmdHandle,"bash -c ... |"), the prefered syntax for execution commands like system or exec, is to let perl parsing the command line.
Try this (as you've already done):
perl -e 'system("bash -c \"echo hello world\"")'
hello world
perl -e 'system "bash -c \"echo hello world\"";'
hello world
And now better, same but letting perl ensure command line parsing, try this:
perl -e 'system "bash","-c","echo hello world";'
hello world
There are clearly 3 argument of system command:
bash
-c
the script
or little more:
perl -e 'system "bash","-c","echo hello world;date +\"Now it is %T\";";'
hello world
Now it is 11:43:44
as you can see in last purpose, there is no double double-quotes enclosing bash script part of command line.
**Nota: on command line, using perl -e '...' or perl -e "...", it's a little heavy to play with quotes and double-quotes. In a script, you could mix them:
system 'bash','-c','for ((i=10;i--;));do printf "Number: %2d\n" $i;done';
or even:
system 'bash','-c','for ((i=10;i--;));do'."\n".
'printf "Number: %2d\n" $i'."\n".
'done';
Using dots . for concatening part of (script part) string, there are always 3 arguments.

Is there a way to launch emacs merge without first opening emacs and using M-x and more?

I sometimes want to merge multiple pairs of files, suppose I want to merge fileA.old and fileA.new, as well as fileB.old and fileB.new..and so on.Currently I have to open emacs. Do M-x ediff-merge-files and enter name of first file, return key, name of second file, return key..and im in merge mode...is there a way to launch emacs with both file names as arguments and land in merge mode?
You can pass Lisp code to Emacs through the command line:
emacs --eval '(ediff-merge-files "path/to/file1" "path/to/file2")'
Of course this could be wrapped in a script to make it more convenient to call. For instance, in a bourne shell, you could do a simple version like this:
#!/bin/sh
# check correct invocation
if [ $# != 2 ]; then
echo "USAGE: $(basename "${0}") <file1> <file2>"
exit 1
fi
# check that file1 exists and is readable
if [ -f "${1}" ]; then
if [ ! -r "${1}" ]; then
echo "Cannot open '${1}', access denied."
exit 3
fi
else
echo "File not found: '${1}'"
exit 2
fi
# check that file2 exists and is readable
if [ -f "${2}" ]; then
if [ ! -r "${2}" ]; then
echo "Cannot open '${2}', access denied."
exit 5
fi
else
echo "File not found: '${2}'"
exit 4
fi
# invoke emacs
emacs --eval "(ediff-merge-files \"${1}\" \"${2}\")"
If you save this in a file ediff on your $PATH, you can then simply write:
ediff file1 file2
on the command line and Emacs will pop up with the two given files in ediff-mode.

How can I check if ldconfig is in the PATH, using bash?

I have scrapped a perl snippet off the web for use in my bash scrip and for reasons too long to go into, it will be better if I could achieve what it tries to do directly in bash.
Here is the script:
bash stuff
...
perl <<'EOF'
use 5.006;
use strict;
use warnings;
if (! can_run("ldconfig")) {
die "you need to have ldconfig in your PATH env to proceed.\n";
}
# check if we can run some command
sub can_run {
my ($cmd) = #_;
#warn "can run: #_\n";
my $_cmd = $cmd;
return $_cmd if -x $_cmd;
return undef;
EOF
more bash stuff
Basically, the question could be rephrased as , "how can I check if ldconfig is in the PATH env using bash?"
You want bash's builtin type command:
if type -P ldconfig; then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Expressed negatively:
if ! type -P ldconfig; then
echo "ldconfig is not in the PATH"
fi
A more straightforward solution would be to invoke the shell and the which command:
$path = `which ldconfig`;
if ($path) {
...
}
If ldconfig is recognised, the path to its executable will be returned, empty output otherwise.
Or, if this Perl script is not going to do anything more than that, you can dismiss it and execute the same command from bash.
I refined #glenn jackman's answer to make it "quiet". It worked as it was but it output "/sbin/ldconfig" to the screen in addition to the echo when in the path. With this modification, only the echo is output:
type ldconfig &>/dev/null
if [ "$?" -eq 0 ]
then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Thanks to all.