# How to Make Your Bioinformatics Scripts Executable with chmod

The single most common reason a bioinformatics script will not run is a missing execute bit. You write a script, save it, type `./qc_reads.sh`, and the shell answers with `bash: ./qc_reads.sh: Permission denied`. The file exists, the code is fine, and the fix takes one command: `chmod +x qc_reads.sh`. The first thing to check in almost every case is the file's permission string with `ls -l`, which shows whether the execute bit is set for you, your group, and everyone else.

This guide explains how file permissions work on Linux systems, how to use `chmod` correctly, why executable scripts matter for [reproducible bioinformatics](/blog/guides/reproducible-bioinformatics-files-environments-parameters-and-provenance), and how to diagnose every common failure mode from the most frequent to the rarest. The commands here apply to any Linux distribution, to macOS, and to the command-line environments on high-performance computing (HPC) clusters where most genomic and metabolomic work is submitted.

## Why Executable Scripts Matter in Bioinformatics

Modern bioinformatics runs on scripts. A typical analysis chains quality control, alignment, and variant calling through shell scripts, Python programs, and workflow engines. Reviews of pipeline development describe small-scale analyses built from Python, R, and notebooks as adequate for a single user, while large, shareable pipelines depend on well-structured scripts and scientific workflow systems [1]. Even purpose-built pipelines that hide complexity from the user still execute through bash scripts and batch schedulers underneath. PePApipe, for example, is a Python pipeline for African Swine Fever Virus [genome analysis](/blog/guides/genome-analysis) that runs locally through bash scripts or through a Slurm protocol on a cluster [2].

The barrier shows up early for newcomers. Surveys of pipeline tools note that many bioinformatics resources require familiarity with shell scripting, the UNIX directory structure, and version control before any analysis can begin [3]. Making a script executable is usually the first encounter with that gap. An educational resource for metabolomics, Metabonaut, builds its entire teaching approach around executable example workflows that run from raw files to annotated feature tables, treating runnable scripts as a core part of FAIR and reproducible practice [4].

When a script cannot be executed, the practical consequences are immediate. Wrapper scripts fail. Cluster job submissions abort. Published workflows that colleagues try to rerun stop at the first line. The admixr package documentation describes how traditional ADMIXTOOLS workflows required users to generate configuration files through shell scripting and manual editing before the package automated those steps [5]. Scripts that drive those configuration steps must run reliably, and reliable execution starts with correct permissions.

## How Linux File Permissions Work

Every file on a Linux or macOS filesystem carries three types of permission for three categories of user. The permissions are read (r), write (w), and execute (x). The user categories are the owner (u), the group (g), and others (o), together abbreviated as all (a).

Run `ls -l myscript.sh` and you will see a string like this:

```
-rw-r--r-- 1 you labgroup 2048 Nov 11 09:15 myscript.sh
```

Reading that string from left to right:

- The first character is the file type. A hyphen means a regular file, and d means a directory.
- The next three characters are the owner's permissions. Here, `rw-` means the owner can read and write but not execute.
- The middle three are the group's permissions, `r-` in this case.
- The last three are everyone else's, also `r-`.

Because the execute bit is absent, the kernel refuses to run the file as a program, and your shell reports `Permission denied`. That is the mechanism behind the error you see most often.

Permissions also have a numeric form. Read counts as 4, write as 2, and execute as 1, and the three digits in a mode like 755 represent owner, group, and others. A permission of 755 means the owner can read, write, and execute (4+2+1=7) while the group and everyone else can read and execute (4+1=5). A permission of 644 means the owner can read and write while everyone else can only read, with no execute bit anywhere.

One distinction matters for bioinformatics. The execute permission on a file allows the kernel to run that file as a program. The execute permission on a directory means something different: it allows you to enter the directory and access the files inside it. If a directory lacks the execute bit for you, `cd` into it fails regardless of the files inside.

## The Shebang Line

Setting the execute bit is only half of the requirement. When you run `./myscript.sh`, the kernel needs to know which interpreter should read the file. The first line of the script, called the shebang, supplies that answer.

For a bash script:

```
#!/bin/bash
```

For a Python script:

```
#!/usr/bin/env python3
```

The `#!/usr/bin/env` form asks the `env` program to locate the interpreter in your PATH, which makes the script portable across systems where Python lives in different directories. This matters on clusters, where conda environments and module systems place interpreters in nonstandard locations. Without a correct shebang, an executed script either fails with a confusing interpreter error or, for a shell script with no shebang at all, gets handed to the shell in ways that can behave unexpectedly.

A missing or malformed shebang is the second most common cause of failure after a missing execute bit, and it produces a different error message, which the troubleshooting table below covers.

## Making a Script Executable, Step by Step

![Five steps from writing the shebang to running a bash script with the dot-slash prefix.](/article-images/illustrations/chmod-make-script-executable-bioinformatics-chmod-steps.webp)

Here is the complete sequence for a new bash script called `qc_reads.sh`:

1. Create or open the script with any editor, for example `nano qc_reads.sh`.
2. Make the first line `#!/bin/bash`.
3. Save the file.
4. Check current permissions with `ls -l qc_reads.sh`. A new file typically shows `-rw-r-r-`.
5. Add the execute bit with `chmod +x qc_reads.sh`.
6. Verify with `ls -l qc_reads.sh`. The string should now show `-rwxr-xr-x`, and many terminals color executable files differently.
7. Run it with `./qc_reads.sh`.

The `./` prefix matters. When you type a bare command name, the shell searches your PATH, not the current directory, for security reasons. `./qc_reads.sh` explicitly says run the file in this directory. This is standard behavior on virtually all modern distributions.

For a numeric mode that sets a script to the conventional 755:

```
chmod 755 qc_reads.sh
```

Both forms accomplish the same result for a fresh script. The symbolic form, `+x`, adds execute to owner, group, and others while leaving read and write untouched. The numeric form sets all nine bits explicitly, which is more predictable when a file starts with unusual permissions.

To make a Python script executable, the sequence is identical except for the shebang:

```
#!/usr/bin/env python3
```

then `chmod +x analyze_variants.py` and `./analyze_variants.py`.

To remove the execute bit, use `chmod -x scriptname`. To set execute for the owner only, useful for private scripts in a home directory, use `chmod u+x scriptname` or `chmod 700 scriptname`.

## Troubleshooting Table

The table below covers the failure modes in the order you are most likely to meet them. Work top to bottom when diagnosing.

| Symptom | Likely cause | How to confirm | Fix |
|--|--|--|--|
| `Permission denied` when running `./script.sh` | Execute bit not set | `ls -l script.sh` shows no `x` in the permission string | `chmod +x script.sh` |
| `command not found` when running `./script.sh` | Missing or wrong shebang, or a leading space or byte-order mark before `#!` | `head -1 script.sh`, and check for hidden bytes with `file script.sh` | Add `#!/bin/bash` or `#!/usr/bin/env python3` as the very first line with no blank line above it |
| `bash: script.sh: No such file or directory` | Wrong path, or missing `./` prefix | `pwd` and `ls script.sh` to confirm the file is where you think | Run `./script.sh` from the script's directory or use the full path |
| `bad interpreter: /bin/bash^M: No such file or directory` | Windows line endings, carriage return characters from editing on Windows | `file script.sh` reports text with CRLF line terminators | Convert with `dos2unix script.sh` or `sed -i 's/\r$//' script.sh` |
| Script runs for you but not for a colleague on a shared drive | Permissions limited to owner, or group membership mismatch | `ls -l` and `id` to compare users and groups | `chmod g+rx script.sh`, and confirm group ownership with `ls -l` |
| Script runs interactively but fails inside a pipeline or cluster job | No execute bit on a called script, or a different PATH inside the job environment | Check the job log for `Permission denied` on a specific file | Set execute bits in the job setup, and use full paths or `#!/usr/bin/env` |
| `Permission denied` even after `chmod +x` | Filesystem mounted with the `noexec` option, or files on media that ignores permission changes | `mount | grep $(df . | tail -1 | awk '{print $1}')` to see mount options | Move the script to a normal filesystem, or have the administrator remount without `noexec` |
| Permission changes vanish after `git clone` or `git pull` | Git tracks the executable bit only as a single mode flag, and new files default to non-executable | `git ls-files -s script.sh` shows mode 100644 instead of 100755 | `git update-index, chmod=+x script.sh` and commit, or run `chmod +x` after checkout |
| `Text file busy` when modifying or rerunning | A copy of the script is currently executing | `ps aux | grep script.sh` | Wait for the running copy to finish or kill it, then edit |
| `Operation not permitted` when running chmod | You do not own the file, or NFS root squashing remaps your access | `ls -l` shows another owner | Ask the owner to run chmod, or use `sudo` if policy allows and you understand the change |
| Script runs but produces bizarre errors about syntax | The file was chmodded but has no shebang, so a different shell interprets it | Compare the error against the language you wrote in | Add the correct shebang and retest |

## Causes from Most Common to Rarest

### Missing execute bit

This cause accounts for the large majority of failures. New files created by editors, downloaded from the web, copied from data archives, or written by Python programs do not carry the execute bit. The default comes from the system umask, a setting that subtracts permission bits from every new file. A typical umask of 022 produces 644 files, readable and writable by the owner and readable by everyone else, with no execute permission. That default is deliberate, since most new files are data, not programs. Every script you create needs the bit added once, explicitly.

### Missing or wrong shebang

The shebang must be the first two bytes of the file. A blank line, a comment, or a UTF-8 byte-order mark ahead of it breaks detection. A shebang pointing to an interpreter that does not exist on the machine, such as `#!/usr/bin/python` on a system where Python 3 lives elsewhere, fails at execution time. The `#!/usr/bin/env interpreter` form avoids hard-coded paths and is the portable choice for Python, Rscript, and other interpreters.

### Wrong path or missing dot-slash prefix

Typing `myscript.sh` without `./` makes the shell search PATH, and unless the current directory is in PATH, which it should not be, the search fails with `command not found`. Typing the wrong filename, especially with autocorrected extensions like `.sh.txt`, fails with `No such file or directory`. Both are simple path errors rather than permission problems, but they appear in the same troubleshooting sessions.

### Windows line endings

Scripts edited on Windows or transferred through tools that add carriage returns carry `\r\n` line endings. The kernel reads the shebang line including the stray carriage return and tries to find an interpreter named `/bin/bash\r`, which does not exist. The error message `bad interpreter` with a visible `^M` character is the signature. `dos2unix` fixes the file in place. This failure is common in collaborative projects where scripts are edited across operating systems.

### Ownership and group mismatches on shared storage

Laboratory shares, network-attached storage, and cluster scratch filesystems host scripts used by many people. If the script is owned by one user and the group execute bit was never set, colleagues receive `Permission denied`. Setting `chmod g+rx` and confirming group ownership with `chgrp` resolves it. On NFS (Network File System) servers, identity mapping between client machines can also make you appear as a different numeric user, which is a job for the storage administrator.

### Filesystems mounted with noexec

Some systems mount removable media, scratch directories, or web-served directories with the `noexec` option, which forbids executing any binary or script on that filesystem regardless of permission bits. `chmod +x` succeeds, `ls -l` shows the bit, and execution still fails. Confirming the mount options with `mount` identifies the cause. The fix is to place scripts on a filesystem mounted normally.

### Git stripping the execute bit

Git records exactly one permission distinction, whether a file is executable, as mode 100755 versus 100644. A script chmodded locally but never committed with the executable flag arrives non-executable after a clone on another machine. The one-command fix is `git update-index, chmod=+x script.sh` followed by a commit. Repositories that distribute analysis scripts to collaborators, which is standard practice for the pipelines described in the workflow literature [1], hit this regularly.

### Interpreter environments on clusters

On HPC systems, the interpreter named in the shebang must be visible inside the job environment, not just your interactive shell. A script with `#!/usr/bin/env python3` executed under a Slurm allocation may find a different Python, or none, if the job script did not load the matching module or activate the conda environment. The failure looks like an interpreter error rather than a permission error. Loading the environment within the job script itself, before execution, prevents it.

## A Short Diagnostic Sequence

![Path through Permission denied, command not found and No such file or directory errors.](/article-images/illustrations/chmod-make-script-executable-bioinformatics-permission-triage.webp)

When a script refuses to run, follow this order:

1. Read the exact error message. `Permission denied` points at permissions. `command not found` points at PATH or the shebang. `No such file or directory` points at the path.
2. Run `ls -l scriptname` and read the permission string. Look for `x` in the owner position.
3. Run `head -1 scriptname` and confirm the shebang is the first line, correct, and free of stray characters.
4. Run `file scriptname` to check line endings and file type.
5. Try `bash scriptname` or `python3 scriptname` directly. If that works but `./scriptname` does not, the problem is permissions or shebang, not the code.
6. If the bit is set and the shebang is correct but execution still fails, check mount options and ownership.

This sequence resolves nearly every case in under a minute.

## chmod and Reproducible Pipelines

Permissions are not just a one-time fix. They are part of how scripts travel between machines and between researchers, which is central to reproducible analysis. The workflow-systems literature frames scripts and pipelines as artifacts that must be shareable, maintainable, and reusable across users and clusters [1]. Educational resources formalize this by shipping executable workflows rather than static code listings [4].

Three practical habits connect chmod to reproducibility.

First, commit the execute bit in version control. Use `git update-index, chmod=+x` for scripts that should be executable everywhere, so a fresh clone produces a working repository without manual repair.

Second, include permission setup in installation steps. Pipelines designed for users with limited command-line experience, such as those that bundle installation, data handling, and execution into guided workflows [3] [2], succeed partly because the user never has to debug permissions by hand. If you distribute your own scripts, a one-line setup command or a Makefile target that runs `chmod +x` on the right files removes an entire class of support questions.

Third, prefer `#!/usr/bin/env` shebangs and explicit interpreter invocations inside pipeline code. Automation layers that wrap command-line tools, the way admixr wraps ADMIXTOOLS configuration and parsing behind R functions [5], depend on the underlying executables being callable from any working directory and any job environment.

## Alternatives to chmod

Setting the execute bit is the right approach when a script will be run repeatedly and distributed. Two alternatives exist for one-off situations, and knowing them helps you interpret errors.

Invoking the interpreter directly, as in `bash qc_reads.sh` or `python3 analyze_variants.py`, requires no execute bit at all, because the interpreter itself is the executable program and the script is merely an argument it reads. This is why a script can appear broken with `./script.sh` and work fine with `bash script.sh`. The content is fine. The permission is the problem. Direct invocation is convenient for testing, but for anything shared or automated, fix the permission and the shebang so the script behaves like a normal command.

The `source` command, as in `source envvars.sh`, reads a script in the current shell rather than as a separate process. It is used for files that set environment variables, and it also does not require the execute bit. It is not a substitute for execution, since it cannot run a program in isolation.

## A Note on sudo and Privileged Changes

Running `chmod` with `sudo` changes permissions as the system administrator and should be rare in analysis work. Scripts belong in your own directories, where you already own the files. Reach for sudo only when a script must live in a system location such as `/usr/local/bin`, and prefer asking your system administrator when you are on a shared cluster. Never set the setuid bit (chmod 4755) on a shell script. On most modern systems the kernel ignores setuid on scripts anyway, and where it does not, it creates a serious security hole. Likewise, avoid `chmod 777`, which grants write access to every user on the system and invites accidental or malicious modification of analysis code. Use 755 for scripts others need to run, and 700 for private scripts.

## Common Mistakes and Limitations

**Chmodding a script with no shebang and assuming it will work.** The execute bit makes the kernel attempt execution. Without a shebang, the kernel cannot identify an interpreter, and behavior varies by system. Always pair `chmod +x` with a correct first line.

**Fixing the symptom instead of the cause.** Wrapping every invocation in `bash script.sh` because the execute bit is missing works, but it hides the problem from collaborators and from workflow engines that call scripts as executables. Set the bit.

**Granting permissions too broadly.** World-writable scripts are a real hazard on shared systems. Anyone who can write the file can change what it does before you run it. Use 755, not 777.

**Forgetting that directories need the execute bit too.** A colleague who cannot `cd` into your scripts directory has a directory permission problem, not a file permission problem.

**Assuming permissions persist through every transfer.** Copying files to a FAT-formatted USB drive, which stores no Unix permissions, or cloning from a repository that never recorded the bit, silently drops the execute flag. Reapply and, for git, commit it.

**Testing only in your own shell.** A script that runs in your terminal can still fail in a cluster job with a different environment. Test the execution path your pipeline will actually use, including the scheduler environment.

**Confusing execute on files with execute on directories.** The same letter means different things, and both cause errors that look similar from the outside.

A final limitation applies to any advice about specific systems. Cluster configurations, NFS settings, and security policies differ between institutions, and a problem that persists after every standard fix here needs your local system administrator.

## Prevention Practices

A small set of habits eliminates most permission problems before they start.

- End every new script with the correct shebang as line one, before anything else, including comments.
- Run `chmod +x` immediately after creating any script, not the first time you try to run it.
- For distributed repositories, set the executable bit in git itself with `git update-index, chmod=+x`, so clones are correct everywhere.
- Add a setup step, such as a Makefile target or a short install script, that runs `chmod +x` on every script in a project.
- Save scripts with Unix line endings. Configure your editor to default to LF, and run `dos2unix` on anything that arrives from Windows.
- Use `#!/usr/bin/env interpreter` shebangs so scripts survive conda environments and module systems.
- Keep scripts out of `noexec`-mounted locations and out of paths with spaces or unusual characters.
- Standardize on 755 for shared scripts and 700 for private ones, and audit with `ls -l` or `find . -name '*.sh' -not -perm -u+x` to catch non-executable scripts before they cause failures.
- Inside pipeline and job scripts, call other scripts by executable path or set the bit programmatically at setup, so the pipeline is self-healing.

## Quick Reference: Linux Commands for Bioinformatics Scripts

| Command | Purpose |
|--|--|
| `chmod +x script.sh` | Add execute permission for owner, group, and others |
| `chmod 755 script.sh` | Set owner to rwx, group and others to rx |
| `chmod 700 script.sh` | Owner only, private script |
| `chmod -x script.sh` | Remove execute permission |
| `ls -l script.sh` | View permissions and ownership |
| `head -1 script.sh` | Verify the shebang line |
| `file script.sh` | Detect line endings and file type |
| `dos2unix script.sh` | Convert Windows line endings |
| `git update-index, chmod=+x script.sh` | Record the executable bit in git |
| `which python3` | Locate an interpreter for the shebang |
| `env | grep PATH` | Inspect the search path in the current environment |
| `umask` | Show the default permission mask for new files |

## Frequently Asked Questions

### What does chmod +x actually do?

It adds the execute permission to a file, telling the Linux kernel that the file may be run as a program. The `+x` form adds execute for the owner, group, and others without touching read or write permissions, which is usually what you want for a shared script.

### Why does bash script.sh work but ./script.sh give Permission denied?

`bash script.sh` runs the bash program, which is already executable, and merely reads your script as input, so no execute bit is needed. `./script.sh` asks the kernel to execute the script file itself, which requires the execute bit and a shebang line. The contrast between the two errors is the fastest way to confirm a permission problem.

### What permissions should I use for my scripts, 755 or 777?

Use 755. It gives you full control and gives everyone else read and execute access without write access. Avoid 777 because it lets any user on the system modify your script, which risks both accidental damage and tampering with analysis code on shared systems.

### Why does my script lose executable permissions after git clone?

Git stores only a single executable flag per file, and files added without that flag arrive non-executable after cloning. Set the flag in the repository with `git update-index, chmod=+x script.sh`, commit, and push so every future clone gets the correct mode.

### How do I make a Python script executable?

Add `#!/usr/bin/env python3` as the first line, run `chmod +x myscript.py`, then execute it with `./myscript.py`. The env-based shebang finds the Python interpreter in your PATH, which keeps the script working across conda environments and cluster module systems.

### What is a shebang and why do I need one?

A shebang is the first line of a script, beginning with `#!`, that names the interpreter which should run the file, such as `#!/bin/bash` for shell scripts or `#!/usr/bin/env python3` for Python. When you execute a script directly, the kernel reads this line to pick the right interpreter, and without it execution fails or runs under the wrong program.

### Does chmod work on Windows, or only Linux and macOS?

The chmod command is a Unix tool and works on Linux and macOS. Windows filesystems such as NTFS and FAT do not store Unix permission bits, so chmod does not apply there natively. On Windows, scripts are typically run by invoking the interpreter directly, such as `python script.py`, or through the Windows Subsystem for Linux, where chmod works normally.

### Can chmod cause problems on an HPC cluster or shared drive?

Yes, in three main ways. Filesystem mounts with the `noexec` option refuse to run scripts no matter what permissions they carry. Network storage may remap user identities so your chmod does not grant access to the colleagues you intended. And batch schedulers such as Slurm run jobs in a different environment, so scripts and interpreters must be executable and findable inside the job, not only in your interactive shell. When standard fixes fail on shared systems, ask your system administrator.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What does chmod +x actually do?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "It adds the execute permission to a file, telling the Linux kernel that the file may be run as a program. The +x form adds execute for the owner, group, and others without touching read or write permissions, which is usually what you want for a shared script."
      }
    },
    {
      "@type": "Question",
      "name": "Why does bash script.sh work but ./script.sh give Permission denied?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "bash script.sh runs the bash program, which is already executable, and merely reads your script as input, so no execute bit is needed. ./script.sh asks the kernel to execute the script file itself, which requires the execute bit and a shebang line. The contrast between the two errors is the fastest way to confirm a permission problem."
      }
    },
    {
      "@type": "Question",
      "name": "What permissions should I use for my scripts, 755 or 777?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use 755. It gives you full control and gives everyone else read and execute access without write access. Avoid 777 because it lets any user on the system modify your script, which risks both accidental damage and tampering with analysis code on shared systems."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my script lose executable permissions after git clone?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Git stores only a single executable flag per file, and files added without that flag arrive non-executable after cloning. Set the flag in the repository with git update-index, chmod=+x script.sh, commit, and push so every future clone gets the correct mode."
      }
    },
    {
      "@type": "Question",
      "name": "How do I make a Python script executable?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Add #!/usr/bin/env python3 as the first line, run chmod +x myscript.py, then execute it with ./myscript.py. The env-based shebang finds the Python interpreter in your PATH, which keeps the script working across conda environments and cluster module systems."
      }
    },
    {
      "@type": "Question",
      "name": "What is a shebang and why do I need one?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A shebang is the first line of a script, beginning with #!, that names the interpreter which should run the file, such as #!/bin/bash for shell scripts or #!/usr/bin/env python3 for Python. When you execute a script directly, the kernel reads this line to pick the right interpreter, and without it execution fails or runs under the wrong program."
      }
    },
    {
      "@type": "Question",
      "name": "Does chmod work on Windows, or only Linux and macOS?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The chmod command is a Unix tool and works on Linux and macOS. Windows filesystems such as NTFS and FAT do not store Unix permission bits, so chmod does not apply there natively. On Windows, scripts are typically run by invoking the interpreter directly, such as python script.py, or through the Windows Subsystem for Linux, where chmod works normally."
      }
    },
    {
      "@type": "Question",
      "name": "Can chmod cause problems on an HPC cluster or shared drive?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, in three main ways. Filesystem mounts with the noexec option refuse to run scripts no matter what permissions they carry. Network storage may remap user identities so your chmod does not grant access to the colleagues you intended. And batch schedulers such as Slurm run jobs in a different environment, so scripts and interpreters must be executable and findable inside the job, not only in your interactive shell. When standard fixes fail on shared systems, ask your system administrator."
      }
    }
  ]
}
</script>

## Sources

1. [Developing and reusing bioinformatics data analysis pipelines using scientific workflow systems.](https://pubmed.ncbi.nlm.nih.gov/36968012/)
2. ['PePApipe': A complete bioinformatics analysis pipeline for African Swine Fever Virus genome.](https://pubmed.ncbi.nlm.nih.gov/42721146/)
3. [FetchPA: a guided, end-to-end solution for local ATAC-Seq data processing and analyses.](https://pubmed.ncbi.nlm.nih.gov/42780185/)
4. [Metabonaut: an open educational resource for learning reproducible, script-based untargeted LC-MS/MS metabolomics.](https://pubmed.ncbi.nlm.nih.gov/42829392/)
5. [admixr-R package for reproducible analyses using ADMIXTOOLS.](https://pubmed.ncbi.nlm.nih.gov/30668635/)