How to create "string" alias that can be combined with normal bash command? - linux

Suppose I already defined
alias scpatoz='scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com'
in my ~/.bash_profile. But sometimes I want to copy the a directory inside abc#helloworld.com to my computer. Normally I would do:
scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com:/home/a .
This takes a long time to type.
How can define the alias (the above definition does not work) so that I can do something like
scpatoz:/home/a .
? And then when a want to copy other directories, then I can also do:
scpatoz:/home2/b .

You could use a small function such as this:
scpatoz()
{
if [ $# -eq 1 ]; then
scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com:$1 .
elif [ $# -gt 1 ]; then
scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com:$#
else
echo "Needs argument"
fi
}
then you could run scpatoz /home/a or add another argument to specify target location

Enter your alias scpatoz or scpatoz:/home/a ., then press Ctrl+Alt+E to expand it, before running the command.

You can convert your command into a script like :
Script Name (al.sh) :
#!/bin/bash
scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com:$1 .
And then create an alias like :
alias scp="sh al.sh"
chmod +x al.sh # Give execute permission
You can run this alias like :
scp /home/a
This will create a command like below and run :
scp -r abcdefghijklmnopqrstuvwxyz#helloworld.com:/home/a .
This way whatever value you give to scp command like in our above case /home/a it will substitute the value of $1 and run the scp command.

Start with a variable name like
atozhost='abcdefghijklmnopqrstuvwxyz#helloworld.com'
Now you might be happy with typing
scp -r ${atozhost}:/home/a .
When you want it even shorter, make a function like #Lepr answered. Use the variable atozhost in the function. I wouldn't use a function but look in the command history and edit the command (using the arrow keys or CONTROL-R and start typing rcp).

Related

How to alias an option?

I am making a bash script where I want to alias some commands. I want to alias rm to, let's say ls:
alias rm="ls"
But then I also want to add custom options to my aliased rm command. I want to add -u. I can't do (for example):
alias rm -u="ls dir1"
The code above throws an error. My question is, how do I alias options to commands? So how do I make custom options? Thanks in advance.
As stated in the comments; altering the function of rm could result in unexpected behaviour; use with caution
The Bash documentation states: 1
For almost every purpose, shell functions are preferred over aliases.
A bash function that will override rm -u could look like this:
rm() {
if [[ "$#" == "-u" ]]; then
ls dir1
else
ls
fi
}
Where
rm -u will run ls dir1
Every other rm * will run ls
You do not want to do this method, but: anyway, you can do:
$ alias somerm="ls "
$ alias -- -u=dir1
$ somerm -u
ls: cannot access 'dir1': No such file or directory
The trailing space in an alias causes alias to be applied on the next word on the command line, in this case on -u, which alias substitutes for dir1.
How do I make custom options?
Options:
write your own utility from scratch with the added option
patch the utility with your own option and distribute it (preferably with a different name and configure your shell to use your own utility instead of standard one)
create a wrapper around the utility to "catch" the new options and apply custom semantics to the utility (ie. a function, as in the other answer).
rm as a function, using (IMO) proper option parsing:
rm () {
local OPTIND OPTARG
local real_command="ls"
local command_args=()
while getopts :u opt; do
case $opt in
u) command_args+=( "dir1" ) ;;
?) echo "unknown option: -$OPTARG" >&2
return 1
;;
esac
done
shift $((OPTIND - 1))
"$real_command" "${command_args[#]}" "$#"
}
Just a smidge more verbose than an alias, but that's the way bash is.

Custom command in linux to move files

I'm trying to create a command that allows me move a file to another directory. For example when I enter "move file1" in the command line, it should move the file "file1" to another directory. I know it can simply done as mv file1 /path/to/destination, But I want to create a new command. I'm kind of new Linux user, please help me.
This is what I tried:
Created an alias for move='/home/bin/move.sh'
So, now when I type move in the command line, it goes and execute move.sh script
Started writing a shell script move.sh as :
#!/bin/bash
mv "$2" "/path/to/destination"
I'm not knowing how to proceed. The whole process might be wrong too. Please help me solve this.
Thanks in advance
Create a function:
move () { mv -t /path/to/destination "$#" ;}
put it in ~/.bashrc to make it permanent.
Now run it as :
move /source /files
From here you can read on how to select arguments.
From here you can read more on how to check for number of arguments.
move.sh
#!/bin/bash
if (( $# < 2 )); then
# TODO: print usage
exit 1
fi
mv "$1" "$2"
Then you will need to make it executable.
chmod u+x move.sh
You can remove the .sh part. It wont change anything.
mv move.sh move
And then you should be able to call the file
move asd /home/
Just make sure that the alias calls the correct file.
If you want to make life easier delete the alias and place the file in the /bin/ directory
cp move /bin/
Good luck.

How to touch a file and mkdir if needed in one line

I need to touch a file with an absolute file name such as: /opt/test/test.txt, but I'm not sure if there is /opt/test existed on the system. So the code should similar with this:
if (-d '/opt/test') {
touch '/opt/test/test.txt';
} else {
mkdir -p '/opt/test';
touch '/opt/test/test.txt'
}
Is there any better way to simplify the code? I hope there is some system commands that can do the same job with only one line.
mkdir B && touch B/myfile.txt
Alternatively, create a function:
mkfile() {
mkdir -p $( dirname "$1") && touch "$1"
}
Execute it with 1 arguments: filepath. Saying:
mkfile B/C/D/myfile.txt
would create the file myfile.txt in the directory B/C/D.
In a shell script, you can simply do:
mkdir -p /opt/test && touch /opt/test/test.txt
mkdir -p will not fail (and won't do anything) if the directory already exists.
In perl, use make_path from the File::Path module, then create the file however you want. make_path also doesn't do anything if the directory exists already, so no need to check yourself.
In perl, using one of my favorite module: Path::Tiny.
path("/opt/test/test.txt")->touchpath;
From the doc:
Combines mkpath and touch. Creates the parent directory if it doesn't
exist, before touching the file.
I like typing very little, so I put this command into a named fn in my .profile, but I used this formulation for years before I did it:
mkdir -p dirname/sub/dir && touch $_/filename.ext
The variable $_ stores the last argument to the previous command. Pretty handy to know about overall.
I defined a touchp in my ~/.bash_aliases:
function touchp() {
/bin/mkdir -p "$(dirname "$1")/" && /usr/bin/touch "$1"
}
It silently creates the structure above the file if not present, and is perfectly safe to use when passed a single filename without any directory in front of it.
Perl from command line,
perl -MFile::Basename -MFile::Path=make_path -e'
make_path(dirname($_)), open(F, ">>", $_) for pop;
' /opt/test/test.txt
I have this shell function in my .zshalias file:
function touch-safe {
for f in "$#"; do
[ -d $f:h ] || mkdir -p $f:h && command touch $f
done
}
alias touch=touch-safe
If either the test or the mkdir command fail, no touch command is invoked.
Bring Python to command line.
i.e. Use pyp
cat filepaths.txt | pyp "'mkdir -p '+s[0:-1]|s+'; touch '+o" | sh
The Pyed Piper", or pyp, is a linux command line text manipulation tool similar to awk or sed, but which uses standard python string and list methods as well as custom functions evolved to generate fast results in an intense production environment.

prompt list of files before execution of rm

I started using "sudo rm -r" to delete files/directories. I even put it as an alias of rm.
I normally know what I am doing and I am quite experience linux user.
However, I would like that when I press the "ENTER", before the execution of rm, a list of files will show up on the screen and a prompt at the end to OK the deletion of files.
Options -i -I -v does not do what I want. I want only one prompt for all the printed files on screen.
Thank you.
##
# Double-check files to delete.
delcheck() {
printf 'Here are the %d files you said you wanted to delete:\n' "$#"
printf '"%s"\n' "$#"
read -p 'Do you want to delete them? [y/N] ' doit
case "$doit" in
[yY]) rm "$#";;
*) printf 'No files deleted\n';;
esac
}
This is a shell function that (when used properly) will do what you want. However, if you load the function in your current shell then try to use it with sudo, it won't do what you expect because sudo creates a separate shell. So you'd need to make this a shell script…
#!/bin/bash
… same code as above …
# All this script does is create the function and then execute it.
# It's lazy, but functions are nice.
delcheck "$#"
…then make sure sudo can access it. Put it in some place that is in the sudo execution PATH (Depending on sudo configuration.) Then if you really want to execute it precisely as sudo rm -r * you will still need to name the script rm, (which in my opinion is dangerous) and make sure its PATH is before /bin in your PATH. (Also dangerous). But there you go.
Here's a nice option
Alias rm to echo | xargs -p rm
The -p option means "interactive" - it will display the entire command (including any expanded file lists) and ask you to confirm
It will NOT ask about the recursively removed files. But it will expand rm * .o to:
rm -rf * .o
rm -rf program.cc program.cc~ program program.o backup?... # NO NO NO NO NO!
Which is much nicer than receiving the error
rm: .o file not found
Edit: corrected the solution based on chepner comment. My previous solutions had a bug :(
This simple script prompts for a y response before deleting the files specified.
rmc script file:
read -p "ok to delete? " ans
case $ans in
[yY]*) sudo rm "$#" ;;
*) echo "Nothing deleted";;
esac
Invoke thus
./rmc *.tmp
I created a script to do this. The solution is similar to #kojiro's.
Save the script with the filename del. Run the command sudo chmod a=r+w+x del to make the script an executable. In the directory in which you want to save the script, export the path by entering export PATH=$PATH:/path/to/the/del/executable in your '~/.bashrc' file and run source ~/.bashrc.
Here, the syntax of rm is preserved, except instead of typing rm ..., type del ... where del is the name of the bash script below.
#! /bin/bash
# Safely delete files
args=("$#") # store all arguments passed to shell
N=$# # number of arguments passed to shell
#echo $#
#echo $#
#echo ${args[#]:0}
echo "Files to delete:"
echo
n=`expr $N - 1`
for i in `seq 0 $n`
do
str=${args[i]}
if [ ${str:0:1} != "-" ]; then
echo $str
fi
done
echo
read -r -p "Delete these files? [y/n] " response
case $response in
[yY][eE][sS]|[yY])
rm ${args[#]:0}
esac

bash - how to pipe result from the which command to cd

How could I pipe the result from a which command to cd?
This is what I am trying to do:
which oracle | cd
cd < which oracle
But none of them works.
Is there a way to achieve this (rather than copy/paste of course)?
Edit : on second thought, this command would fail, because the destination file is NOT a folder/directory.
So I am thinking and working out a better way to get rid of the trailing "/oracle" part now (sed or awk, or even Perl) :)
Edit :
Okay that's what I've got in the end:
cd `which oracle | sed 's/\/oracle//g'`
You use pipe in cases where the command expects parameters from the standard input. ( More on this ).
With cd command that is not the case. The directory is the command argument. In such case, you can use command substitution. Use backticks or $(...) to evaluate the command, store it into variable..
path=`which oracle`
echo $path # just for debug
cd $path
although it can be done in a much simpler way:
cd `which oracle`
or if your path has special characters
cd "`which oracle`"
or
cd $(which oracle)
which is equivalent to backtick notation, but is recommended (backticks can be confused with apostrophes)
.. but it looks like you want:
cd $(dirname $(which oracle))
(which shows you that you can use nesting easily)
$(...) (as well as backticks) work also in double-quoted strings, which helps when the result may eventually contain spaces..
cd "$(dirname "$(which oracle)")"
(Note that both outputs require a set of double quotes.)
With dirname to get the directory:
cd $(which oracle | xargs dirname)
EDIT: beware of paths containing spaces, see #anishpatel comment below
cd `which oracle`
Note those are backticks (generally the key to the left of 1 on a US keyboard)
OK, here a solution that uses correct quoting:
cd "$(dirname "$(which oracle)")"
Avoid backticks, they are less readable, and always quote process substitutions.
You don't need a pipe, you can do what you want using Bash parameter expansion!
Further tip: use "type -P" instead of the external "which" command if you are using Bash.
# test
touch /ls
chmod +x /ls
cmd='ls'
PATH=/:$PATH
if cmdpath="$(type -P "$cmd")" && cmdpath="${cmdpath%/*}" ; then
cd "${cmdpath:-/}" || { echo "Could not cd to: ${cmdpath:-/}"; exit 1; }
else
echo "No such program in PATH search directories: ${cmd}"
exit 1
fi
besides good answer above, one thing needs to mention is that cd is a shell builtin, which run in the same process other than new process like ls which is a command.
https://unix.stackexchange.com/questions/50022/why-cant-i-redirect-a-path-name-output-from-one-command-to-cd
http://en.wikipedia.org/wiki/Shell_builtin
In response to your edited question, you can strip off the name of the command using dirname:
cd $(dirname `which oracle`)

Resources