Execute line given in output of another command - linux

Warning: this question is not about usage of git, but about usage of pipes in Linux, git command is given here as example.
Having such output of git push
fatal: The current branch my_branch has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin my_branch
I want to execute given command, i.e. git push --set-upstream origin my_branch.
By doing git push 2>&1 | tail -3 I get git push --set-upstream origin my_branch printed on screen.
Question: What command should be added to next pipe so given git push will be executed. So I could do git push 2>&1 | tail -3 | command_to_eval_given_string

Just pipe to bash itself:
git push ... | bash
This will send the stdout from the previous pipes to bash, that will then execute it.
$ echo "echo 'hello'" | bash
hello
$ echo "uptime" | bash
16:22:37 up 7:31, 3 users, load average: 0,03, 0,14, 0,23

Rather than piping to another command, you can instead wrap the pipeline in a command substitution and eval it. This will execute the command in your current shell session. For example:
eval "$(printf 'some output 1\nsome output 2\necho execute me'| tail -1)";
## execute me

Related

In Bash Script Second Argument to Function is not Processed as Expected

Trying to combine git - add/commit/push in one fuction.
gitacp testfile/testfile.py "testing" works okay.
gitacp testfile/testfile.py "testing this" results in this error.
error: pathspec 'this' did not match any file(s) known to git.
From some reason the set of strings in the second argument is not
getting processed correctly.
# Git add, commit and push
function gitacp {
args=("$#")
filepathname=${args[0]}
comment=${args[1]}
branchname=$(git status 2>/dev/null | head -n1 | cut -d" " -f3)
git add ${filepathname}
git commit -m ${comment}
echo ${branchname}
echo ${branchnmrmspc}
echo ${comment}
echo ${filepathname}
git push --set-upstream origin ${branchname}
}
gitacp testfile/testfile.py "testing this" results in this error.
error: pathspec 'this' did not match any file(s) known to git.
Quote the argument
git commit -m "${comment}"

How do I display git output in bash and store one string from the output in a variable?

I am running git push command in bash which generates some errors.
RESPONSE=$(git push "$target" --all | grep "error:" || true)
generates an output on the screen but variable $RESPONSE is empty
If I change the command to do this:
RESPONSE=$(git push "$target" --all 2>&1 | grep "error:" || true)
command runs silently but actually captures needed error in $RESPONSE
echo $RESPONSE
error: failed to push some refs to
'ssh://git#git.test.test.com:7999/test/test-test.git'
I really need to run this git push command in a way that it would hold the error above in $RESPONSE yet generate the entire output on the screen.
Running
RESPONSE=$(git push "$target" --all 2>&1 | tee -a log | grep "error:" || true) did not help, unless I am missing something.
One solution is to use tee; just not exactly the way you showed. Taking it step by step will perhaps make the solution easier to understand:
git push "$target" --all
will send the error you want to STDERR. That's why you added 2>&1, to redirect STDERR to STDOUT.
git push "$target" --all 2>&1
Then your pipeline (grep, etc.) is able to pick it up and eventually the variable capture is able to see it when you do
RESPONSE=$(git push "$target" --all 2>&1 | grep "error:" || true)
But because the error is no longer going to STDERR, and STDOUT is now being captured instead of sent to the screen, the output disappears.
So what you want to use tee for, is to put the output on both STDERR (for the screen) and STDOUT (for your pipeline and eventual variable capture).
RESPONSE=$(git push "$target" --all 2>&1 |tee >(cat 1>&2) | grep "error:" || true)
This will likely work as you intend, but be aware that everything you see on the screen - all output from the git command, error or otherwise - is now being passed on STDERR.
There aren't many practical reasons why this would be better than the answer about capturing to the variable and then echoing the variable (per miimote's answer), but if for some reason the non-sequential command structure seems better to you, this is a way to do it.
The first line
RESPONSE=$(git push "$target" --all | grep "error:" || true)
stores the response of the command in the var RESPONSE. So it is done in bash for any construction like VAR=$(command). If exists an error the var is empty but generates an output for the user.
If you add 2>&1, you are saying the same but if exists an error the output is the file $1, in your case the var $RESPONSE.
You could do this
RESPONSE=$(git push "$target" --all 2>&1 | grep "error:" || true); echo $RESPONSE
You can read more about command substitution and redirections

Find a specific folder in all remote and local GIT branches

I have few hundreds of remote and local branches. I wonder whether there is a command to help me find a folder with a specific name in all branches.
My git version is 1.8.3.1. I also have smartgit installed if it matters.
Thanks in advance.
The following command will output all refs (local and remotes) that point to a commit which contains the path specified in the variable SEARCH_PATH
SEARCH_PATH="somePath"
git for-each-ref --format="%(refname)" refs/heads refs/remotes |
while read ref
do
if [[ `git ls-tree -r --name-only $ref` =~ "$SEARCH_PATH" ]] ; then
echo $ref;
fi
done
You can run following to list your required folders/files
for line in `git for-each-ref --format="%(refname)" refs/heads`;
do
git ls-tree -r $line | grep 'file_regex'
done

Bash environment variable not updating

I'm using the git post-checkout hook in my repo to the current branch into a variable, I then want to use it else where like PHP etc.
Below is my post-checkout script:
#!/bin/bash
echo $GITBRANCH
GITBRANCH=`git symbolic-ref HEAD | cut -d/ -f3-`
echo $GITBRANCH
export $GITBRANCH
However it doesn't update. For example:
>git checkout master
Switched to branch 'master'
develop
master
>echo $GITBRANCH
develop
Running the GITBRANCH=git symbolic-ref HEAD | cut -d/ -f3- command on it's own will then produce the current branch name.
Why doesn't the hook update the $GITBRANCH variable globally?
When you set the variable in a script, it'll be available only in the shell that the scripts runs in. As soon as the process terminates, the variable you set is gone forever!
If you want the variable available everywhere, probably .profile or .bashrc would be a better place.
A two-step process should accomplish what you want:
1) In your post-checkout script, create a temporary file containing the variable you want to export. Something like
#!/bin/bash
GITBRANCH=`git symbolic-ref HEAD | cut -d/ -f3-`
echo "GITBRANCH=$GITBRANCH" > /tmp/new-branch
2) Create a bash function to act as a wrapper around git, and use that to source
the temporary file after a checkout:
# Put this in .bashrc
git () {
command git "$#"
if [[ $1 = "checkout" ]]; then
. /tmp/new-branch
fi
}
$ git checkout master
Switched to branch 'master'
$ echo $GITBRANCH
master
run the script with a dot in front of it.
. script
Try:
export GITBRANCH
That is, without the dollar sign.

Can't add a file separated with space to git

I have been writing a script to add untracked files using git add .
The loop I use in my script is
for FILE in $(git ls-files -o --exclude-standard); do
git add $FILE
git commit -m "Added $FILE"
git push origin master
done
The script runs fine till it faces a filename which has space in it. for Eg., I cant add the file Hello 22.mp4.(Note that there is a SPACE between Hello and 22). The above loop would take the file as 2 separate files, Hello and 22.mp4 and exit with error.
Does someone know how to add it as a single file?
Thanks
What's happening is the shell is expanding the $(...) into a bunch of words, and it's obviously interpreting a file with spaces embedded as multiple files obviously. Even with the prior suggestions of quoting the git add command, it wouldn't work. So the loop is getting run with wrong arguments, as shown by this output with set -x:
ubuntu#up:~/test$ ls -1
a a
ubuntu#up:~/test$ set -x; for FILE in $(git ls-files -o --exclude-standard); do git add "$FILE"; git commit -m "Added $FILE"; done
+ set -x
++ git ls-files -o --exclude-standard
+ for FILE in '$(git ls-files -o --exclude-standard)'
+ git add a
...
The proper solution is to quote the git add $file and have git ls-files NULL separate the filenames by passing -z to git ls-files and use a while loop with a null delimiter:
git ls-files -o --exclude-standard -z | while read -r -d '' file; do
git add "$file"
git commit -m "Added $file"
git push origin master
done
If you are using bash alternative to the solution provided by #AndrewF, you can make use of IFS bash internal variable to change the delimiter from space to newline, something on these lines:
(IFS=$'\n'
for FILE in $(git ls-files -o --exclude-standard); do
git add $FILE
git commit -m "Added $FILE"
git push origin master
done
)
This is just for your information. The response of AndrewF is more informative covering debugging option & usage of while instead of for.
Hope this helps!
Try putting the $FILE var in quotes:
git add "$FILE"
That'll quote the filename, thus allowing spaces in it.
Replace git add $FILE with git add "$FILE". That way it will be interpreted as a single element.
I know that this is very late but here is one way to do it using the standard xargs linux command:
git ls-files -o --exclude-standard | xargs -L 1 -I{} -d '\n' git add '{}'
You can test it by simply echoing the command as follows:
git ls-files -o --exclude-standard | xargs -L 1 -I{} -d '\n' echo "git add '{}'"
To add as a single file add a backslash before the space in the filename:
git add pathtofilename/filenamewith\ space.txt

Resources