How to create symlinks in a specific directory - bash

I'm working on an automated installation of a openSUSE system using AutoYAST, and I'm stumped on a small detail. In order to setup relevant applications in the user's environment, I try to symlink to all applications located in /usr/local/bin in ~/bin (so say /usr/local/bin has the addr2line utility, then I want to have a symlink to that in ~/bin).
I've tried to execute the following snipped to accomplish this:
su -c "for program in `ls /usr/local/bin`; do ln -s /usr/local/bin/$program ~/bin/$program; done" <user>
This snippet executes in the post-script phase of the automatic installation, which is executed as root (and seeing as I want the owner of the symlinks to be the user, this command is executed using su).
However, this does not work, and gives the following output:
++ ls /usr/local/bin
+ su -c 'for program in addr2line
ar
as
c++
c++filt
cpp
elfedit
g++
gcc
gcc-ar
gcc-nm
gcc-ranlib
gcov
gprof
i686-pc-linux-gnu-c++
i686-pc-linux-gnu-g++
i686-pc-linux-gnu-gcc
i686-pc-linux-gnu-gcc-4.9.3
i686-pc-linux-gnu-gcc-ar
i686-pc-linux-gnu-gcc-nm
i686-pc-linux-gnu-gcc-ranlib
ld
ld.bfd
nm
objcopy
objdump
ranlib
readelf
size
strings
strip; do ln -s /usr/local/bin/ ~/bin/; done' <user>
bash: -c: line 1: syntax error near unexpected token `ar'
bash: -c: line 1: `ar'
I've tried several variations of the command, but all seem to not exactly do what I want.
For example, I've also tried:
su -c "for program in /usr/local/bin/*; do ln -s $program ~/bin/; done" <user>
But this only created a symlink to /usr/local/bin in ~/bin.
So I'm a bit stuck on this one... Does anybody have an idea?

You're using double quotes to define your su command, so $program is being evaluated immediately. You want it evaluated when su executes the command. Use single quotes instead:
su -c 'for program in `ls /usr/local/bin`; do ln -s /usr/local/bin/$program ~/bin/$program; done' <user>

You can also use cp -s to create symlinks on a system with GNU cp (like your suse system), which gives you the ability to use recursion and the other fun options of cp.

In the end, I decided to go with the command posted by pacholik to fix this, as my original attempt was over-engineered and thus not necessary.
ln -s /usr/local/bin/* ~/bin

Related

Prevent data leak due to file permissions during installation in UNIX

I install files using standard POSIX utilities cp, install, sed, sh.
It is possible to fix any permission with chmod / chown / chgrp but it is dangerous to temporarily expose sensitive data and fix it later.
"Standard" way to deal with the problem is to use install -m MODE -u USER -g GRP.
What if I need to process file with a "dumb" utility (like grep / sed / awk / sh )? How can I prevent data leak from such tools? By using umask 777?
I consider following dangerous:
base64 -d secret.txt >/etc/app.key
sed -e '/^#.*/d' </etc/default/app.cfg >/etc/app.cfg
because file content might be accessible to other users if umask is too open. Also I have to "fix" user/group after redirections...
PS Seems install is not in POSIX... https://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html
Also GNU install doesn't read from pipe, so following trick is impossible:
sed ... < $SRC | install -m MODE -u USER -g GRP - $DEST
Some shell allow process substitution (<(cmd) syntax) or one might create named pipe as workaround...
After reading POSIX I see that there is no guaranty for mkdir, cp and other tools to respect umask. Actually umask is a process property and is handled by kernel/syscals.
I'd better use non-standard GNU install with -m MODE (-u, -g).
For dumb tools GNU Bash with process substitution would be handy:
install -m 0700 <(sed ... $SRC) $DST
But I'm not sure of FIFO permissions...

/usr/bin/env: ln: Too many levels of symbolic links

This problem is killing me and I feel like I've tried everything.
First off, the problem started happening when upgrading to Capistrano 3. Capistrano now utilizes /usr/bin/env before every command when deploying, to make sure the environment setup is correct.
When Capistrano goes to create symlinks to the necessary shared directory and respective files, it attempts commands like:
/usr/bin/env ln -s /full/path /different/full/path
...and then it errors out:
/usr/bin/env: ln: Too many levels of symbolic links
I realize it's not Capistrano's fault, so I began troubleshooting by ssh'ing to my server and trying the same command, and I receive the same error (which at least is good for consistency). I then try the same command without /usr/bin/env:
ln -s /full/path /different/full/path
And it works!!!! Maybe you can see the real solution that I can't?
here is the output of just the /usr/bin/env command:
rvm_bin_path=/home/deployer/.rvm/bin
GEM_HOME=/home/deployer/.rvm/gems/ruby-1.9.3-p392
TERM=xterm-256color
SHELL=/bin/bash
IRBRC=/home/deployer/.rvm/rubies/ruby-1.9.3-p392/.irbrc
SSH_CLIENT=...
OLDPWD=/home/deployer/Sites/example.com
MY_RUBY_HOME=/home/deployer/.rvm/rubies/ruby-1.9.3-p392
SSH_TTY=/dev/pts/0
USER=deployer
LS_COLORS= .....
_system_type=Linux
rvm_path=/home/deployer/.rvm
SSH_AUTH_SOCK=....
rvm_prefix=/home/deployer
MAIL=/var/mail/deployer
PATH=/home/deployer/.rvm/gems/ruby-1.9.3-p392/bin:/home/deployer/.rvm/gems/ruby-1.9.3-p392#global/bin:/home/deployer/.rvm/rubies/ruby-1.9.3-p392/bin:/home/deployer/.rvm/bin:/opt/rubyee/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/deployer/.rvm/bin
PWD=/home/deployer/Sites
LANG=en_US.UTF-8
_system_arch=i386
_system_version=12.04
rvm_version=1.26.4 (latest)
SHLVL=1
HOME=/home/deployer
LOGNAME=deployer
GEM_PATH=/home/deployer/.rvm/gems/ruby-1.9.3-p392:/home/deployer/.rvm/gems/ruby-1.9.3-p392#global
SSH_CONNECTION=....
LESSOPEN=| /usr/bin/lesspipe %s
LESSCLOSE=/usr/bin/lesspipe %s %s
RUBY_VERSION=ruby-1.9.3-p392
_system_name=Ubuntu
_=/usr/bin/env
I have also tried commands like the following, to find potential symlink loops:
find . -maxdepth 20 -type l -exec ls -ld {} +
But is not producing correct results:
lrwxrwxrwx 1 deployer deployer ...
You might not being using the same ln utility.
When invoking it directly from the interactive shell, ln might be overridden e.g. by an alias or by some shell function ln() {...;}.
This does not happen when /usr/bin/env tries to do that (AFAIK it looks for ln in PATH). I suspect that the ln it finds has issues, so you are getting this error.
This is an example scenario that might be similar to your case:
# start from an empty directory
$ ls -l
total 0
# create a problematic `ln` in the current directory
$ ln -s ln ln
$ ls -l
total 0
lrwxrwxrwx 1 me me 2 Jan 7 20:28 ln -> ln
# have an alias for the "real" ln
$ alias ln=/bin/ln
# mess up PATH
$ PATH="$PWD"
Now let's try the two alternatives, /usr/bin/env goes first:
$ /usr/bin/env ln -s /some/path /tmp/path
/usr/bin/env: ln: Too many levels of symbolic links
Then plain ln (remember that we aliased it):
$ ln -s /some/path /tmp/path
$ echo $?
0
$ /bin/ls -l /tmp/path
lrwxrwxrwx 1 me me 10 Jan 7 20:31 /tmp/path -> /some/path
So my suggestion is: look at issues with ln, e.g. by finding all different alternatives that might be visible. In bash you might run this:
$ type -a ln
Try this to find symlink loops:
find . -follow -printf ""
if you are using docker so you should install ruby in your case
docker run ruby
source
https://github.com/docker/for-win/issues/5763#issuecomment-585749243

Creating a bash alias to su to a new user and source a script

I'd like to create a bash alias that does the following:
su to another user
cd to a certain directory
source another script (specifically a python virtualenv activation script)
After running the alias, I would like to be logged in as the user, in the directory, with the virtualenv activated. I can get everything to work except for the su, which seems to live in its own parallel universe.
I can't see a way to make the cd and source run inside the su environment and stick, leaving me able to continue work inside the su environment. (e.g. su -c just runs the operations and leaves me where I started.)
UPDATE:
To clarify a bit, I want something like this:
sudo -u www-data bash -c 'cd /var/www; pwd; whoami'
But I want to remain in the interactive subshell until I ^D out.
If you don't care about your ~/.bashrc, you can just source your file instead of that:
su -c 'bash --rcfile myfile'
If you do care and your file only exports variables (i.e. doesn't define aliases or completion):
su -c 'source myfile; bash'
If you do need your ~/.bashrc and you need defined aliases and completion, you can source both with:
su -s /bin/bash -c 'bash --rcfile <(echo "source ~/.bashrc; source myfile")'

Can't get shell script to run in Xcode

I'm fairly new to running scripts in Xcode and haven't been able to figure out whats wrong with the script I'm running. The first script I ran was this:
/bin/sh -x
PBXCP=${DEVELOPER_DIR}/Library/PrivateFrameworks/DevToolsCore.framework/Resources/pbxcp
${PBXCP} -exclude .svn "${PROJECT_DIR}/../../base"
"${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"
Which caused me to run into this error:
/Users/newperson/Library/Developer/Xcode/DerivedData/appname-etesgjzdmfzimlgvakidckjecgij
/Build
/Intermediates/appname.build/Debug-iphonesimulator/app.build/Script-
435F41A90F532CA300887552.sh: line 3: /Applications/Xcode.app/Contents/Developer/Library
/PrivateFrameworks/DevToolsCore.framework/Resources/pbxcp: No such file or directory
This error was fairly to the point, The file the script is looking for doesn't exist. The newer versions of Xcode have gotten rid of pbxcp. So I started looking for a good alternative script to run that wouldn't use pbxcp, when I found this:
/bin/sh -x
/usr/bin/tar -c -C "${PROJECT_DIR}/myframeworks" --exclude .DS_Store --exclude CVS --exclude
.svn --exclude .git -H `cd "${PROJECT_DIR}/myframeworks" && find DevToolsCore.framework` |
/usr/bin/tar -x -C ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}
This script also caused me to run into a problem, which was this:
tar: could not chdir to '/Users/newperson/Library/Developer/Xcode/DerivedData/appname-
etesgjzdmfzimlgvakidckjecgij/Build/Products/Debug-iphonesimulator/appname.app/Frameworks'
tar: Write error
Command /bin/sh failed with exit code 1
I couldn't find a clear answer to what this error meant, one forum suggest that I use the sudo command in my script to give the script permission to change directory, so I ran this:
/bin/sh -x
/usr/bin/tar -c -C "${PROJECT_DIR}/myframeworks" --exclude .DS_Store --exclude CVS --exclude
.svn --exclude .git -H `cd "${PROJECT_DIR}/myframeworks" && find DevToolsCore.framework`
| sudo /usr/bin/tar -x -C ${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}
This script caused me to run into this error though:
tar: Write error
Command /bin/sh failed with exit code 1
++ find DevToolsCore.framework
sudo: no tty present and no askpass program specified
tar: Write error
This is as far as I got so far, I am fairly lost with my limited knowledge of shell script so any help correcting my script or finding a suitable replacement for the Xcode framework that contains pbxcp would be appreciated.
Change the permissions of the directory where your script wants to write files. Do it in an interactive session of Terminal:
$ sudo chmod a+w the_directory
Then you should be able to run your script (without sudoing the tar).

wget and run/remove bash script in one line

wget http://sitehere.com/install.sh -v -O install.sh; rm -rf install.sh
That runs the script after download right and then removes it?
I like to pipe it into sh. No need to create and remove file locally.
wget http://sitehere.com/install.sh -O - | sh
I think you might need to actually execute it:
wget http://sitehere.com/install.sh -v -O install.sh; ./install.sh; rm -rf install.sh
Also, if you want a little more robustness, you can use && to separate commands, which will only attempt to execute the next command if the previous one succeeds:
wget http://sitehere.com/install.sh -v -O install.sh && ./install.sh; rm -rf install.sh
I think this is the best way to do it:
wget -Nnv http://sitehere.com/install.sh && bash install.sh; rm -f install.sh
Breakdown:
-N or --timestamping will only download the file if it is newer on the server
-nv or --no-verbose minimizes output, or -q / --quiet for no "wget" output at all
&& will only execute the second command if the first succeeds
use bash (or sh) to execute the script assuming it is a script (or shell script); no need to chmod +x
rm -f (or --force) the file regardless of what happens (even if it's not there)
It's not necessary to use the -O option with wget in this scenario. It is redundant unless you would like to use a different temporary file name than install.sh
You are downloading in the first statement and removing in the last statement.
You need to add a line to excute the file by adding :
./install.sh

Resources