0

This tiny Shell script called prepareFastaRef.command is supposed to receive a file name I am giving as an argument, generate the path based on it, then run a command on that file. It looks like:

#!/bin/bash

REF="~/Dropbox/phd/fastarefs/$1"
echo "$REF"

samtools faidx "$REF"

I run it from the command line with:

prepareFastaRef.command test.fa

But it does not seem to be able to find the file:

[E::fai_build3_core] Failed to open my file ~/Dropbox/phd/fastarefs/test.fa

The problem is not specific to the programme samtools as I have the same issue with others. And I can confirm 100% the file test.fa is in there.

What I find confusing is that this command alone works perfectly:

samtools faidx ~/Dropbox/phd/fastarefs/off_aa1.fa

So it seems to be an issue with how I pass the path from within the Shell script.

What am I missing?

4
  • 3
    Duplicate: Tilde in path doesn't expand to home directory Commented Nov 29, 2019 at 18:00
  • 3
    Use this for your assignment: REF=~/Dropbox/phd/fastarefs/"$1" (Quoting not strictly needed here at all, but good practice in general.) Commented Nov 29, 2019 at 18:01
  • 2
    To diagnose something like this, you can run your script with set -x and you'll see that ~ didn't get expanded. Commented Nov 29, 2019 at 18:02
  • 2
    Or shellcheck.net would have told you, as well. Commented Nov 29, 2019 at 18:20

1 Answer 1

0

This solution assumes you've simplified your code and that $REF is more dynamic than presented. If that is not the case, skip to the bottom for simpler solutions.

#!/bin/bash

REF="~/Dropbox/phd/fastarefs/$1"
if [ "$REF" = "~/${REF#??}" ]; then
  REF="$HOME/${REF#??}"
fi
echo "$REF"

samtools faidx "$REF"

This takes a look at the first two letters of $REF. If they are a tilde and then a slash, that is replaced by $HOME.

With a little more code, you can also implement other home directories like ~root:

#!/bin/bash

REF="~/Dropbox/phd/fastarefs/$1"

# Output the given user's home directory (passwd's 6th colon-delimited field)
user2home() {
  getent passwd "$1" |awk -F: '{ print $6 }'
}

case "$REF" in
  ( ~/* )  REF="$HOME/${REF#??}" ;;
  ( ~* )   REF="${REF#?}"; REF="$(user2home "${REF%%/*}")/${REF#*/}" ;;
esac
echo "$REF"

samtools faidx "$REF"

These also work with /bin/sh (POSIX-compliant, no bashisms). They employ parameter substitution:

  • ${REF#??} is the contents of $REF without its first two characters (like s/^..//)
  • ${REF#?} is the contents of $REF without its first character (like s/^.//)
  • ${REF%%/*} is the contents of $REF truncated before its first slash (like s:/.*::g)
  • ${REF#*/} is the contents of $REF starting after its first slash (like s:^[^/]*/::)

Therefore, given REF="~root/.bashrc", the second case clause would match and $REF would have its first character removed (~root/.bashrcroot/bashrc), then it would be passed to the user2home function with the later path elements removed (root/bashrcroot). The output of user2home would then have a slash and the rest of the path added to it, concatenating /root + / + .bashrc.


Of course, I'm assuming this is a simplified question and that the prefix to $REF isn't hard-coded as it is in this example. If you're doing that, you might as well reduce all of my code down to a simple one-liner:

#!/bin/bash

REF="$HOME/Dropbox/phd/fastarefs/$1"
echo "$REF"

samtools faidx "$REF"

Another way to solve this, as noted by @BenjaminW, would be to pull the tilde out of the quoted string:

#!/bin/bash

REF=~/"Dropbox/phd/fastarefs/$1"
echo "$REF"

samtools faidx "$REF"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.