Вот решение для Perl. Это будет работать на любое количество файлов и ожидает, что первый файл будет списком. Это также добавит имя файла к заголовку FASTA.
#!/usr/bin/perl -w
use strict;
my $list=shift;
open(A,$list);
my %k;
while(<A>){
## Remove trailing newline
chomp;
if ( /(\d+?)_knownids_(.+?)_.+?(\d+)$/){
## Concatenate the patterns and save in a hash
my $pp=join("-", $1,$2,$3);
$k{PAT}{$pp}=$_;
}
}
close(A);
## Read each input file
my $name;
for my $f (@ARGV) {
open(F,$f);
while(<F>){
## Skip empty lines
next if /^\s*$/;
## Is this a FASTA header?
if ( /^\s*>/){
## If this id is in the list, keep it for this file
if(/(\d+?)_knownids_(.+?)_.+?(\d+)$/){
$name=join("-", $1,$2,$3);
}
## Skip the sequences we are not interested in
else{$name="foo"}
}
## Collect the sequence
else {
if (defined($k{PAT}{$name})) {
$k{$f}{$name}.=$_;
}
}
}
close(F);
}
## For each unique pattern found in list.txt
foreach my $pat (keys(%{$k{PAT}})) {
## For each of the files passed as arguments
foreach my $file (@ARGV) {
## If the pattern was found in that file, print
if (defined($k{$file}{$pat})) {
print ">$k{PAT}{$pat}_$file\n";
print "$k{$file}{$pat}"
}
}
}
Если сценарий сохраняется как compare.pl
, сделайте:
$ ./compare.pl list.txt sample1.txt sample2.txt sample3.txt sampleN.txt
Вывод:
> GETID_11084_knownids_3/3_Confidence_0.600_Length_1451_sample2.txt
sampletextforsample2
> GETID_17049_knownids_1/2_Confidence_0.625_Length_2532_sample1.txt
sampletextforsample1
> GETID_17049_knownids_1/2_Confidence_0.625_Length_2532_sample3.txt
sampletextforsample3
> GETID_15916_knownids_10/11_Confidence_0.324_Length_1825_sample3.txt
sample2textforsample3
Это типичное задание для expr
:
$ file=/path/to/abcdef.txt
$ expr "/$file" : '.*\([^/.]\{3\}\)\.[^/.]*$'
def
Если вы знаете, что имена файлов имеют ожидаемый формат (содержит одну и только одну точку и не менее 3 символов перед точкой), то это можно упростить до:
expr "/$file" : '.*\(.\{3\}\)\.'
Обратите внимание, что статус выхода будет ненулевым, если совпадение отсутствует, а также если совпаденная часть является числом, которое разрешается до 0. (как для a000.txt
или a-00. txt
)
С помощью zsh
:
file=/path/to/abcdef.txt
lastpart=${${file:t:r}[-3,-1]}
(:t
для tail (имя базиса), :r
для rest (с удалением расширения))).
var=123456
echo "${var#"${var%???}"}"
###OUTPUT###
456
Что сначала удаляет три последних символа из $var
, а затем удаляет из $var
результаты этого удаления - что возвращает три последних символа из $var
. Вот несколько примеров, более конкретно направленных на демонстрацию того, как это можно сделать:
touch file.txt
path=${PWD}/file.txt
echo "$path"
/tmp/file.txt
base=${path##*/}
exten=${base#"${base%???}"}
base=${base%."$exten"}
{
echo "$base"
echo "$exten"
echo "${base}.${exten}"
echo "$path"
}
file
txt
file.txt
/tmp/file.txt
Не обязательно распространять все это через столько команд. Вы можете сжать это:
{
base=${path##*/} exten=
printf %s\\n "${base%.*}" "${exten:=${base#"${base%???}"}}" "$base" "$path"
echo "$exten"
}
file
txt
file.txt
/tmp/file.txt
txt
Комбинирование $IFS
с set
ting параметрами оболочки также может быть очень эффективным средством для разбора и сверления переменных оболочки:
(IFS=. ; set -f; set -- ${path##*/}; printf %s "${1#"${1%???}"}")
В результате вы получите только три символа, непосредственно предшествующих первому периоду, следующему за последним /
в $path
. Если вы хотите получить только первые три символа, непосредственно предшествующие последнему .
в $path
(например, если существует возможность использования более одного .
в имени файла):
(IFS=.; set -f; set -- ${path##*/}; ${3+shift $(($#-2))}; printf %s "${1#"${1%???}"}")
В обоих случаях вы можете сделать:
newvar=$(IFS...)
И...
(IFS...;printf %s "$2")
... распечатает то, что следует за .
Если вы не возражаете против использования внешней программы, вы можете сделать:
printf %s "${path##*/}" | sed 's/.*\(...\)\..*/\1/'
Если есть вероятность, что в имени файла \n
имеется символ ewline (не применимо для нативных решений оболочки - все они все равно обрабатывают это):
printf %s "${path##*/}" | sed 'H;$!d;g;s/.*\(...\)\..*/\1/'
sed
работает для этого:
[user@host ~]$ echo one.two.txt | sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|'
two
Или
[user@host ~]$ sed -r 's|(.*)\..*$|\1|;s|.*(...)$|\1|' <<<one.two.txt
two
Если ваш sed
не поддерживает -r
, просто замените экземпляры ()
на \(
и \)
, и тогда -r
не понадобится.
Если perl доступен, я считаю, что он может быть более читабельным, чем другие решения, а именно потому, что его язык регексов более выразителен и имеет модификатор /x
, который позволяет писать более четкие регексы:
perl -e 'print $1 if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
Это ничего не печатает, если такого совпадения нет (если у базового имени нет расширения или если корень перед расширением слишком короткий). В зависимости от ваших требований, вы можете настроить регекс. Этот регекс обеспечивает соблюдение ограничений:
Использование этого в подстановке команд имеет обычные проблемы с удалением слишком большого количества трейлинговых новых строк, проблема, которая также влияет на ответ Стефана. Она может быть решена в обоих случаях, но здесь немного проще:
lastpart=$(
perl -e 'print "$1x" if shift =~ m{ ( [^/]{3} ) [.] [^./]* \z }x' -- "$file"
)
lastpart=${lastpart%x} # allow for possible trailing newline
Python2. 7
$ echo /path/to/somefile.txt | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
ile
$ echo file.one.two.three | python -c "import sys, os; print '.'.join(os.path.basename(sys.stdin.read()).split('.')[:-1])[-3:]"
two
Я думаю, что эта функция bash, pathStr(), сделает то, что вы ищете.
Она не требует awk, sed, grep, perl или expr. Она использует только bash-строки, так что она довольно быстрая.
Я также включил зависимые функции argsNumber и isOption, но их функциональность может быть легко встроена в pathStr.
Зависимая функция ifHelpShow не включена, так как она имеет множество зависимостей для вывода текста справки либо в командной строке терминала, либо в GUI-диалоговом окне через YAD. Переданный ему текст справки включен в документацию. Сообщите, если вы хотите ifHelpShow и его зависимости.
function pathStr () {
ifHelpShow "$1" 'pathStr --OPTION FILENAME
Given FILENAME, pathStr echos the segment chosen by --OPTION of the
"absolute-logical" pathname. Only one segment can be retrieved at a time and
only the FILENAME string is parsed. The filesystem is never accessed, except
to get the current directory in order to build an absolute path from a relative
path. Thus, this function may be used on a FILENAME that does not yet exist.
Path characteristics:
File paths are "absolute" or "relative", and "logical" or "physical".
If current directory is "/root", then for "bashtool" in the "sbin" subdirectory ...
Absolute path: /root/sbin/bashtool
Relative path: sbin/bashtool
If "/root/sbin" is a symlink to "/initrd/mnt/dev_save/share/sbin", then ...
Logical path: /root/sbin/bashtool
Physical path: /initrd/mnt/dev_save/share/sbin/bashtool
(aka: the "canonical" path)
Options:
--path Absolute-logical path including filename with extension(s)
~/sbin/file.name.ext: /root/sbin/file.name.ext
--dir Absolute-logical path of directory containing FILENAME (which can be a directory).
~/sbin/file.name.ext: /root/sbin
--file Filename only, including extension(s).
~/sbin/file.name.ext: file.name.ext
--base Filename only, up to last dot(.).
~/sbin/file.name.ext: file.name
--ext Filename after last dot(.).
~/sbin/file.name.ext: ext
Todo:
Optimize by using a regex to match --options so getting argument only done once.
Revised:
20131231 docsalvage' && return
#
local _option="$1"
local _optarg="$2"
local _cwd="$(pwd)"
local _fullpath=
local _tmp1=
local _tmp2=
#
# validate there are 2 args and first is an --option
[[ $(argsNumber "$@") != 2 ]] && return 1
! isOption "$@" && return 1
#
# determine full path of _optarg given
if [[ ${_optarg:0:1} == "/" ]]
then
_fullpath="$_optarg"
else
_fullpath="$_cwd/$_optarg"
fi
#
case "$_option" in
--path) echo "$_fullpath" ; return 0;;
--dir) echo "${_fullpath%/*}" ; return 0;;
--file) echo "${_fullpath##*/}" ; return 0;;
--base) _tmp1="${_fullpath##*/}"; echo "${_tmp1%.*}" ; return 0;;
--ext) _tmp1="${_fullpath##*/}";
_tmp2="${_tmp1##*.}";
[[ "$_tmp2" != "$_tmp1" ]] && { echo "$_tmp2"; }
return 0;;
esac
return 1
}
function argsNumber () {
ifHelpShow "$1" 'argsNumber "$@"
Echos number of arguments.
Wrapper for "$#" or "${#@}" which are equivalent.
Verified by testing on bash 4.1.0(1):
20140627 docsalvage
Replaces:
argsCount
Revised:
20140627 docsalvage' && return
#
echo "$#"
return 0
}
function isOption () {
# isOption "$@"
# Return true (0) if argument has 1 or more leading hyphens.
# Example:
# isOption "$@" && ...
# Note:
# Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
# from 'isOption "$@"' where first argument in "$@" is '--help'
# Revised:
# 20140117 docsalvage
#
# support both short and long options
[[ "${1:0:1}" == "-" ]] && return 0
return 1
}
RESOURCES
Если вы можете использовать perl
:
lastpart=$(
perl -e 'print substr((split(/\.[^.]*$/,shift))[0], -3, 3)
' -- "$(basename -- "$1")"
)