using xargs to pass a variable to alias command - linux

I'm trying to write a one liner that creates an alias 'cd="cd dir_name"' which will change directory to to that dir_name
pwd | xargs -i alias cd{}='cd $PWD'
but I get:
xargs: alias: No such file or directory
is it that alias cannot be played with xargs or am I not using xargs correctly?

alias is a shell builtin. xargs needs an external command to run. Normally, you can run a new shell in xargs to interpret the builtins or keywords:
pwd | xargs -i bash -c 'alias cd{}="cd $PWD"'
but it's useless in this case, as the alias would live only in the shell you run from xargs, not in the current one.
Moreover, alias can't be named /home/user. Maybe you meant
... alias cd='cd {}'
Use pushd and popd to remember the current directory and return to it later.

Related

How to copy without getting prompt for overwrite (overwrite all) all hidden files, folders and subfolders from one folder to anotherin linux? [duplicate]

I'm trying to use the cp command and force an overwrite.
I have tried cp -rf /foo/* /bar, but I am still prompted to confirm each overwrite.
You can do yes | cp -rf xxx yyy, but my gutfeeling says that if you do it as root - your .bashrc or .profile has an alias of cp to cp -i, most modern systems (primarily RH-derivatives) do that to root profiles.
You can check existing aliases by running alias at the command prompt, or which cp to check aliases only for cp.
If you do have an alias defined, running unalias cp will abolish that for the current session, otherwise you can just remove it from your shell profile.
You can temporarily bypass an alias and use the non-aliased version of a command by prefixing it with \, e.g. \cp whatever
This is probably caused by cp being already aliased to something like cp -i. Calling cp directly should work:
/bin/cp -rf /zzz/zzz/* /xxx/xxx
Another way to get around this is to use the yes command:
yes | cp -rf /zzz/zzz/* /xxx/xxx
As some of the other answers have stated, you probably use an alias somewhere which maps cp to cp -i or something similar. You can run a command without any aliases by preceding it with a backslash. In your case, try
\cp -r /zzz/zzz/* /xxx/xxx
The backslash will temporarily disable any aliases you have called cp.
You probably have an alias somewhere, mapping cp to cp -i; because with the default settings, cp won't ask to overwrite. Check your .bashrc, your .profile etc.
See cp manpage: Only when -i parameter is specified will cp actually prompt before overwriting.
You can check this via the alias command:
$ alias
alias cp='cp -i'
alias diff='diff -u'
....
To undefine the alias, use:
$ unalias cp
As other answers have stated, this could happend if cp is an alias of cp -i.
You can append a \ before the cp command to use it without alias.
\cp -fR source target
So I run into this a lot because I keep cp aliased to cp -iv, and I found a neat trick. It turns out that while -i and -n both cancel previous overwrite directives, -f does not. However, if you use -nf it adds the ability to clear the -i. So:
cp -f /foo/* /bar <-- Prompt
cp -nf /foo/* /bar <-- No Prompt
Pretty neat huh? /necropost
By default cp has aliase to cp -i. You can check it, type alias and you can see some like:
alias cp='cp -i'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
To solve this problem just use /bin/cp /from /to command instead cp /from /to
The simplest way for me:
yes | cp source destination
you can use this command as well:
cp -ru /zzz/zzz/* /xxx/xxx
it would update your existing file with the newer one though.
cp is usually aliased like this
alias cp='cp -i' # i.e. ask questions of overwriting
if you are sure that you want to do the overwrite then use this:
/bin/cp <arguments here> src dest
I found this
'cp' -rf * /data/danalonso_testing/target/
Source: https://superuser.com/questions/358843/how-to-replace-all-the-contents-from-one-folder-with-another-one/358851
cp -u ...
cp --update ...
also works.
Another way to call the command without the alias is to use the command builtin in bash.
command cp -rf /zzz/zzz/*
-n is "not to overwrite" but his question is totally opposite what you replied for.
To avoid this confirmation you can simply run the cp command wiht absolute path, it will avoid the alias.
/bin/cp sourcefile destination
If you want to keep alias at the global level as is and just want to change for your script.
Just use:
alias cp=cp
and then write your follow up commands.
I simply used unalias to remove the "cp -i" alias, then do the copy, then set back the alias. :
unalias cp
cp -f foo foo.copy
alias cp="cp -i"
Not the most beautiful code, but easy to set and efficient. I also check the alias is already set back with a simple
alias |grep cp
If this is a small text file, you may consider this way too:
cat my.cnf > /etc/my.cnf
Not sure about the efficiency or side effects for large or binary files.
It is not cp -i. If you do not want to be asked for confirmation,
it is cp -n; for example:
cp -n src dest
Or in case of directories/folders is:
cp -nr src_dir dest_dir

bash aliases in xargs: "bash -c" does not pass arguments to command

To recognize aliases in "xargs", I have set an alias
alias xargs="xargs bash -ic"
If I now execute the below snippet, no arguments are passed to the command to xargs.
find . -name pom.xml | xargs grep projectid
Infact, no arguments are passed to the command even in this case.
bash -ic grep projectid pom.xml
The documentation for bash says
-c If the -c option is present, then commands are read from the first non-option
argument command_string. If there are arguments after the command_string, they
are assigned to the positional parameters, starting with $0.
So what am I doing wrong?
bash --version
GNU bash, version 4.3.39(2)-release (x86_64-unknown-cygwin)
UPDATE:
Thanks to #knittl for his inputs. A work around solution for now to avoid all the extra punctuations in #knittl's answer.
1. Download xargs_bash_alias.sh
2. Set an alias
alias xargs="<path>/xargs_bash_alias.sh"
Now your xargs commands would recognize your other bash aliases.
There are two things you need to be aware of. First, proper quoting:
find . -name pom.xml -print0 | xargs -0 bash -c "grep projectid"
Second, you need to pass your positional arguments somehow:
find . -name pom.xml -print0 | xargs -0 bash -c 'grep projectid "$#"' -
Use - as first argument to bash, so positional arguments start at $1, just like in a normal shellscript.
"$#" expands to quoted positional arguments starting from 1.
Since xargs passes multiple arguments at once, you either need to use "$#" (quoted!) inside your bash script or run xargs with the -n1 option.

About the usage of linux command "xargs"

I have some file like
love.txt
loveyou.txt
in directory useful; I want to copy this file to directory /tmp.
I use this command:
find ./useful/ -name "love*" | xargs cp /tmp/
but is doesn't work, just says:
cp: target `./useful/loveyou.txt' is not a directory
when I use this command:
find ./useful/ -name "love*" | xargs -i cp {} /tmp/
it works fine,
I want to know why the second works, and more about the usage of -i cp {}.
xargs puts the words coming from the standard input to the end of the argument list of the given command. The first form therefore creates
cp /tmp/ ./useful/love.txt ./useful/loveyou.txt
Which does not work, because there are more than 2 arguments and the last one is not a directory.
The -i option tells xargs to process one file at a time, though, replacing {} with its name, so it is equivalent to
cp ./useful/love.txt /tmp/
cp ./useful/loveyou.txt /tmp/
Which clearly works well.
When using the xargs -i command, {} is substituted with each element you find. So, in your case, for both "loveyou.txt" and "love.txt", the following command will be run:
cp ./useful/loveyou.txt /tmp/
cp ./useful/love.txt /tmp/
if you omit the {}, all the elements you find will automatically be inserted at the end of the command, so, you will execute the nonsensical command:
cp /tmp/ ./useful/loveyou.txt ./useful/love.txt
xargs appends the values fed in as a stream to the end of the command - it does not run the command once per input value. If you want the same command run multiple times - that is what the -i cp {} syntax is for.
This works well for commands which accept a list of arguments at the end (e.g. grep) - unfortunately cp is not one of those - it considers the arguments you pass in as directories to copy to, which explains the 'is not a directory' error.
The first example will do this:
cp /tmp/ love.txt loveyou.txt
Which can't be done, since they attempt to copy the directory /tmp and the file love.txt to the file loveyou.txt.
In the second example, -i tells xargs to replace every instance of {} with the argument, so it will do:
cp love.txt /tmp/
cp loveyou.txt /tmp/
find ./useful/ -name "love*" | xargs cp -t /tmp/
You might avoid xargs that way:
find ./useful/ -name "love*" -exec sh -c 'cp "$#" /tmp' sh {} +

Using a while/for loop with the 'find' command to copy files and directories

I have the follwoing problem: I want to make a script that backups a certain directory completely to another directory. I may not use cp -r or any other recursive command. So I was thinking of using a while or for loop. The directory that needs to be back upped is given with a parameter. This is what I have so far:
OIFS="$IFS"
IFS=$'\n'
for file in `find $1`
do
cp $file $HOME/TestDirectory
done
IFS="$OIFS"
But when I execute it, this is what my terminal says: Script started, file is typescript
Try this:
find "$1" -type f -print0 | xargs -0 cp -t $HOME/TestDirectory
Don't run your script through script !
Add this (shebang) at top of file:
#!/bin/bash
find "$1" -type f -print0 | xargs -0 cp -t $HOME/TestDirectory
change permission of your script for adding executable flag:
chmod +x myScript
run your script localy whith arguments:
./myScript rootDirectoryWhereSearchForFiles

How can I use aliased commands with xargs?

I have the following alias in my .aliases:
alias gi grep -i
and I want to look for foo case-insensitively in all the files that have the string bar in their name:
find -name \*bar\* | xargs gi foo
This is what I get:
xargs: gi: No such file or directory
Is there any way to use aliases in xargs, or do I have to use the full version:
find -name \*bar\* | xargs grep -i foo
Note: This is a simple example. Besides gi I have some pretty complicated aliases that I can't expand manually so easily.
Edit: I used tcsh, so please specify if an answer is shell-specific.
Aliases are shell-specific - in this case, most likely bash-specific. To execute an alias, you need to execute bash, but aliases are only loaded for interactive shells (more precisely, .bashrc will only be read for an interactive shell).
bash -i runs an interactive shell (and sources .bashrc).
bash -c cmd runs cmd.
Put them together:
bash -ic cmd runs cmd in an interactive shell, where cmd can be a bash function/alias defined in your .bashrc.
find -name \*bar\* | xargs bash -ic gi foo
should do what you want.
Edit: I see you've tagged the question as "tcsh", so the bash-specific solution is not applicable. With tcsh, you dont need the -i, as it appears to read .tcshrc unless you give -f.
Try this:
find -name \*bar\* | xargs tcsh -c gi foo
It worked for my basic testing.
This solution worked perfect for me in bash:
https://unix.stackexchange.com/a/244516/365245
Problem
[~]: alias grep='grep -i'
[~]: find -maxdepth 1 -name ".bashrc" | xargs grep name # grep alias not expanded
[~]: ### no matches found ###
Solution
[~]: alias xargs='xargs ' # create an xargs alias with trailing space
[~]: find -maxdepth 1 -name ".bashrc" | xargs grep name # grep alias gets expanded
# Name : .bashrc
Why it works
[~]: man alias
alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.
Turn "gi" into a script instead
eg, in /home/$USER/bin/gi:
#!/bin/sh
exec /bin/grep -i "$#"
don't forget to mark the file executable.
The suggestion here is to avoid xargs and use a "while read" loop instead of xargs:
find -name \*bar\* | while read file; do gi foo "$file"; done
See the accepted answer in the link above for refinements to deal with spaces or newlines in filenames.
This is special-character safe:
find . -print0 | xargs -0 bash -ic 'gi foo "$#"' --
The -print0 and -0 use \0 or NUL-terminated strings so you don't get weird things happening when filenames have spaces in them.
bash sets the first argument after the command string as $0, so we pass it a dummy argument (--) so that the first file listed by find doesn't get consumed by $0.
For tcsh (which does not have functions), you could use:
gi foo `find -name "*bar*"`
For bash/ksh/sh, you can create a function in the shell.
function foobar
{
gi $1 `find . -type f -name "*"$2"*"`
}
foobar foo bar
Remember that using backquotes in the shell is more advantageous than using xargs from multiple perspectives. Place the function in your .bashrc.
Using Bash you may also specify the number of args being passed to your alias (or function) like so:
alias myFuncOrAlias='echo' # alias defined in your ~/.bashrc, ~/.profile, ...
echo arg1 arg2 | xargs -n 1 bash -cil 'myFuncOrAlias "$1"' arg0
(should work for tcsh in a similar way)
# alias definition in ~/.tcshrc
echo arg1 arg2 | xargs -n 1 tcsh -cim 'myFuncOrAlias "$1"' arg0 # untested
The simplest solution in you case would be to expand your alias inline. But that is valid for csh/tcsh only.
find -name \*bar\* | xargs `alias gi` foo
for bash it will be more tricky, not so handy but still might be useful:
find -name \*bar\* | xargs `alias gi | cut -d "'" -f2` foo
After trying many solutions with xargs that didn't work for me, went for an alternative with a loop, see examples below:
for file in $(git ls-files *.txt); do win2unix $file; done
for file in $(find . -name *.txt); do win2unix $file; done
Put your expression that generates a list of files inside $() as in the examples above. I've used win2unix which is a function in my .bashrc that takes a file path and converts it to Linux endings. Would expect aliases to also work.
Note that I did not have spaces in my paths or filenames.

Resources