Łączenie dwóch zmiennych ze znakiem podkreślenia

54

Muszę połączyć dwie zmienne, aby utworzyć nazwę pliku z podkreśleniem. Wywołuje moje zmienne $FILENAMEi $EXTENSIONgdzie nazwa pliku jest odczytywana z pliku.

FILENAME=Hello
EXTENSION=WORLD.txt

Teraz...

Próbowałem następujących bez powodzenia:

NAME=${FILENAME}_$EXTENSION
NAME=${FILENAME}'_'$EXTENSION
NAME=$FILENAME\\_$EXTENSION

Zawsze otrzymuję coś dziwnego. Zwykle podkreślenie jest pierwsze.

Muszę tak być

echo $NAME
Hello_WORLD.txt
Rhyuk
źródło
2
Prawdopodobnie twój skrypt zawiera znaki powrotu karetki.
Stéphane Chazelas
Tak to było. : D
Rhyuk

Odpowiedzi:

67

Możesz użyć czegoś takiego:

NAME=$(echo ${FILENAME}_${EXTENSION})

Działa to również:

NAME=${FILENAME}_${EXTENSION}
Tim
źródło
1
Problemem był plik, z którego czytałem NAME. Formatowanie systemu Windows ... Twoje rozwiązanie działało świetnie. Dzięki
Rhyuk
2
Pierwsze polecenie zmienia wartość zmiennej i użycie podstawienia polecenia i echonie może pomóc. Drugie polecenie zostało już podane w pytaniu.
Gilles 'SO - przestań być zły'
1
Tak! Kocham to. Długo mnie to denerwowało. Dzięki za rozwiązanie!
racl101,
To działa dla mnie. Kiedy używam „$ FILENAME_ $ EXTENSION”, pojawiają się tylko wartości „$ EXTENSION”. Czy mógłbyś pomóc wyjaśnić, dlaczego tak się dzieje? Dzięki
Lei Hao
12

Twój:

NAME=${FILENAME}_$EXTENSION
NAME=${FILENAME}'_'$EXTENSION

wszystko w porządku, więc byłoby:

NAME=${FILENAME}_${EXTENSION}
NAME=$FILENAME'_'$EXTENSION
NAME=$FILENAME\_$EXTENSION
NAME=$FILENAME"_$EXTENSION"

(ale z pewnością nie NAME=$(echo ${FILENAME}_${EXTENSION}) za używaecho i operatora ).split+glob

NAME=$FILENAME_$EXTENSION

byłby taki sam jak:

NAME=${FILENAME_}${EXTENSION}

as _(w przeciwieństwie do \lub ') jest poprawnym znakiem w nazwie zmiennej.

Problem polega na tym, że linie rozdzielane są za pomocą CRLF zamiast tylko LF, co oznacza, że ​​te zmienne treści kończą się znakiem CR.

Znak CR po zapisaniu na terminalu informuje terminal, aby przesunął kursor na początek linii. Hello<CR>_WORLD.TXT<CR>Gdy więc zostanie wysłany do terminala, pokaże się jako _WORLD.TXT(przesłaniający Hello).

Stéphane Chazelas
źródło
1

Przełączyłem się na użycie ${FILENAME}_${EXTENSION}wyżej wspomnianej składni.

Kiedyś $()potrzebowałem, kiedy musiałem połączyć dwie zmienne z podkreśleniem. Na przykład, $YYYYMMDD$()_$HHMMSSaby wygenerować nazwę pliku zawierającą znacznik czasu w formacie RRRRMMDD_GGMMSS. Środek $()nic nie zwraca i dzieli dwie zmienne na części.

Kiedyś mierzyłem timezadania przy użyciu $YYYYMMDD$()_$HHMMSSmetody i niektórych z powyższych, i wszyscy zgłosili 0ms na starym serwerze, na którym używam tego. Wydajność nie wydaje się być problemem.

użytkownik208145
źródło
To nie odpowiada na pytanie. Powinieneś w związku z tym zadać nowe pytanie. $()rozwidla podpowłokę na kilka powłok (niektóre optymalizują ją). Przejmuje także porwanie operatora za coś, dla czego nie został zaprojektowany. ${x}_yjest tym czego chcesz.
Stéphane Chazelas
Co masz na myśli: „To nie odpowiada na pytanie”. To nie jest świetna odpowiedź, ale to odpowiedź.
Scott
Edytowane w celu usunięcia niejednoznaczności.
user208145,
0

Powinieneś używać zmiennych małymi literami (jest to najlepsza praktyka).
Więc użyję nazwy pliku i rozszerzenia zamiast FILENAME i EXTENSION

Mówiąc, że „nazwa pliku jest odczytywana z pliku”, zakładam, że skrypt to:

read -r filename  <file.txt
extension=World.txt

I że chcesz połączyć obie zmienne $ filename i $ extension z podkreśleniem _.
Podane przykłady (z wyjątkiem: brak podwójnego \) działają tutaj poprawnie:

name=${filename}_$extension
name=${filename}'_'$extension
name=$filename\_$extension

Jak niektóre inne:

name="${filename}"'_'"${extension}"
name="$filename"'_'"$extension"
name="${filename}_${extension}"

Twój problem nie polega na tym, jak zmienne są sklejone, ale na zawartości zmiennych. Wydaje się rozsądne sądzić, że to:

read -r filename  <file.txt

odczyta końcowy powrót karetki, \rjeśli będzie czytać z pliku Windows.

Jednym prostym rozwiązaniem (dla ksh, bash, zsh) jest usunięcie wszystkich znaków kontrolnych ze zmiennej read:

filename=${filename//[[:cntrl:]]/}

Można to zasymulować za pomocą wartości, która ma znak powrotu karetki:

$ filename=$'Hello\r'
$ echo "starting<$filename>end"
>endting<Hello                       ### note the return to the start of line.
$ echo "starting<${filename//[[:cntrl:]]/}>end"
starting<Hello>end

Lub zastępując wartość filename:

$ filename="${filename//[[:cntrl:]]/}"
$ echo "start<$filename>end"
start<Hello>end

Wniosek.

Więc to:

name="${filename//[[:cntrl:]]/}_${extension//[[:cntrl:]]/}"

Otrzyma poprawną wartość, namenawet jeśli inne zmienne zawierają znaki kontrolne.


źródło
-2

Możesz także użyć tego: oddziel oddzielne zmienne spacjami i połącz je za pomocą tr, aby usunąć spację.

TableNameToCreate="MyTable"
# concatenation work is done here 
$(echo "$TableNameToCreate _my_other_Info " | tr -d " ")

#Proof
echo "Var to create with var pasted as prefix to suffix
    $(echo "$TableNameToCreate _my_other_Info " | tr -d " ") 
. "

# create a new var
NewVar=$(echo "$TableNameToCreate _my_other_Info " | tr -d " ")

#proof
echo "$NewVar"
Greg
źródło
(1) Na pytanie to udzielono już więcej niż wystarczającej odpowiedzi. (2) Co robi twoja odpowiedź, jeśli w istniejącej zmiennej znajdują się znaki specjalne?
G-Man,
Nie rozwiązuje to problemu ze zmienną zawierającą znak powrotu karetki. Usuwa również spacje, czego może nie chcieć.
Kusalananda
@Kusalananda: Właściwie, chyba że coś przeoczyłem, to dobrze radzi sobie ze zwrotem karetki; spacje są jedynym problemem.
G-Man,