0

I'm trying to create a bash script that finds all the files in my dotfiles directory, and symlinks them into ~. If the directory a file is in does not exist, it shall be created.

The script as it is now is just trying to find files and create the two paths, when that works "real" and "symlink" will be used with ln -s. However when trying to save the strings in "real" and "symlink" all i get is line 12: ./.zshrc: Permission denied

What am I doing wrong?

#!/bin/bash

dotfiles=()

readarray -d '' dotfiles < <(find . -type f ! -path '*/.git/*' ! -name "README.md" -type f -print0)

for file in ${dotfiles[@]}; do
                dir=$(dirname $file | sed s#.#$HOME#1)
                [ ! -d $dir ] && echo "directory $dir not exist!" && mkdir -p $dir
                
                # Create path strings
                real=$("$file" | sed s#.#$PWD#1)
                symlink=$("$file" | sed s#.#$HOME#1)
                echo "Real path: $cur"
                echo "Symbolic link path: $new"
done

exit

P.S, I'm a bash noob and am mostly doing this script as a learning experience.

dlundv
  • 3
  • 2

1 Answers1

0

Here is a refactoring which attempts to fix the obvious problems. See comments with # ... for details. Probably also check with http://shellcheck.net/ before you ask for human assistance.

The immediate reason for the error message is that $("$file" ...) does indeed attempt to run $file as a command and capture its output. You probably meant $(echo "$file" ...) but that can often be avoided.

#!/bin/bash

dotfiles=()
readarray -d '' dotfiles < <(find . -type f ! -path '*/.git/*' ! -name "README.md" -type f -print0)

# ... Fix broken quoting throughout
for file in "${dotfiles[@]}"; do
    dir=$(dirname "$file" | sed "s#^\.#$HOME#")  # ... 1 is not required or useful
    # ... Use standard error for diagnostics
    [ ! -d "$dir" ] && echo "$0: directory $dir does not exist!" >&2 && mkdir -p "$dir"
    
    # Create path strings
    # ... Use parameter substitution
    real=$PWD/${file#./}
    symlink=$HOME/${$file#./}
    # ... You mean $real, not $cur?
    echo "Real path: $real"
    # ... You mean $symlink, not $new?
    echo "Symbolic link path: $symlink"
done
# ... No need to explicitly exit; trust me, you will anyway
#exit

These are just syntactic fixes; I would probably avoid storing the results from find in an array and just loop over them directly, and I haven't checked if the logic actually does what (we might guess) you are perhaps trying to do.

See also Looping over pairs of values in bash for a similar topic, and When to wrap quotes around a shell variable?.

The script still has a pesky assumption that it will be run in the dotfiles directory. Probably a better design would be to explicitly run find in that directory, and refactor accordingly.

sed 's/x/y/' will replace the first occurrence of x with y on each line by definition and by design; there is no reason or need to explicitly add 1 after the final delimiter. (Some sed variants allow a number there to select a different match than the first, but this is not portable; and of course specifying the first match when that's already the default is simply silly. There is a g flag to replace all matches instead of just the first which many beginners want to use everywhere, but of course that's not necessary or useful here either.)

tripleee
  • 175,061
  • 34
  • 275
  • 318