Can't pipe in bash's âmapfileâ ⦠but why?
Clash Royale CLAN TAG#URR8PPP
up vote
8
down vote
favorite
I just want to get all the files in a certain directory into a bash array (assuming that none of the files have a newline in the name):
So:
myarr=()
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]"
Empty result!
If I do the roundabout way of using a file, temporary or otherwise:
myarr=()
find . -maxdepth 1 -name "mysqldump*" > X
mapfile -t myarray < X
echo "$myarray[@]"
Result!
But why doesn't mapfile
read properly from a pipe?
bash
 |Â
show 2 more comments
up vote
8
down vote
favorite
I just want to get all the files in a certain directory into a bash array (assuming that none of the files have a newline in the name):
So:
myarr=()
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]"
Empty result!
If I do the roundabout way of using a file, temporary or otherwise:
myarr=()
find . -maxdepth 1 -name "mysqldump*" > X
mapfile -t myarray < X
echo "$myarray[@]"
Result!
But why doesn't mapfile
read properly from a pipe?
bash
5
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
2
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
Why usingfind
andmapfile
here at all and not just simplymyarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.
â BlackJack
Aug 15 at 14:43
1
Just noticed that one has to turnnullglob
option on (shopt -s nullglob
) on formyarr=(mysqldump*)
to not end up with the array('mysqldump*')
in case no files match.
â David Tonhofer
Aug 16 at 8:52
 |Â
show 2 more comments
up vote
8
down vote
favorite
up vote
8
down vote
favorite
I just want to get all the files in a certain directory into a bash array (assuming that none of the files have a newline in the name):
So:
myarr=()
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]"
Empty result!
If I do the roundabout way of using a file, temporary or otherwise:
myarr=()
find . -maxdepth 1 -name "mysqldump*" > X
mapfile -t myarray < X
echo "$myarray[@]"
Result!
But why doesn't mapfile
read properly from a pipe?
bash
I just want to get all the files in a certain directory into a bash array (assuming that none of the files have a newline in the name):
So:
myarr=()
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]"
Empty result!
If I do the roundabout way of using a file, temporary or otherwise:
myarr=()
find . -maxdepth 1 -name "mysqldump*" > X
mapfile -t myarray < X
echo "$myarray[@]"
Result!
But why doesn't mapfile
read properly from a pipe?
bash
asked Aug 14 at 11:13
David Tonhofer
630720
630720
5
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
2
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
Why usingfind
andmapfile
here at all and not just simplymyarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.
â BlackJack
Aug 15 at 14:43
1
Just noticed that one has to turnnullglob
option on (shopt -s nullglob
) on formyarr=(mysqldump*)
to not end up with the array('mysqldump*')
in case no files match.
â David Tonhofer
Aug 16 at 8:52
 |Â
show 2 more comments
5
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
2
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
Why usingfind
andmapfile
here at all and not just simplymyarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.
â BlackJack
Aug 15 at 14:43
1
Just noticed that one has to turnnullglob
option on (shopt -s nullglob
) on formyarr=(mysqldump*)
to not end up with the array('mysqldump*')
in case no files match.
â David Tonhofer
Aug 16 at 8:52
5
5
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
2
2
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
Why using
find
and mapfile
here at all and not just simply myarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.â BlackJack
Aug 15 at 14:43
Why using
find
and mapfile
here at all and not just simply myarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.â BlackJack
Aug 15 at 14:43
1
1
Just noticed that one has to turn
nullglob
option on (shopt -s nullglob
) on for myarr=(mysqldump*)
to not end up with the array ('mysqldump*')
in case no files match.â David Tonhofer
Aug 16 at 8:52
Just noticed that one has to turn
nullglob
option on (shopt -s nullglob
) on for myarr=(mysqldump*)
to not end up with the array ('mysqldump*')
in case no files match.â David Tonhofer
Aug 16 at 8:52
 |Â
show 2 more comments
3 Answers
3
active
oldest
votes
up vote
21
down vote
accepted
From man 1 bash
:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Such subshells inherit variables from the main shell but they are independent. This means mapfile
in your original command operates on its own myarr
. Then echo
(being outside the pipe) prints empty myarr
(which is the main shell's myarr
).
This command works differently:
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]";
In this case mapfile
and echo
operate on the same myarr
(which is not the main shell's myarr
).
To change the main shell's myarr
you have to run mapfile
in the main shell exactly. Example:
myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "$myarr[@]"
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
add a comment |Â
up vote
10
down vote
Bash runs the commands of a pipeline in a subshell environment, so any variable assignments etc. that take place within it aren't visible to the rest of the shell.
Dash (Debian's /bin/sh
) as well as busybox's sh
are similar, while zsh and ksh
run the last part in the main shell. In Bash, you can use shopt -s lastpipe
to do the same, but it only works when job control is disabled, so not in interactive shells by default.
So:
$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b
(read
and mapfile
have the same issue.)
Alternatively (and as mentioned by Attie), use process substitution, which works like a generalized pipe, and is supported in Bash, ksh and zsh.
$ bash -c 'x=a; read x < <(echo b); echo $x'
b
POSIX leaves it unspecified if the parts of a pipeline run in subshells or not, so it can't really be said that any of the shells would be "wrong" in this.
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
add a comment |Â
up vote
8
down vote
As Kamil has pointed out, each element in the pipeline is a separate process.
You can use the following process substitution to get find
to run in a different process, with the mapfile
invocation remaining in your current interpreter, allowing access to myarr
afterwards:
myarr=()
mapfile -t myarr < <( find . -maxdepth 1 -name "mysqldump*" )
echo "$myarr[@]"
b < <( a )
will act similarly to a | b
in terms of how the pipeline is wired - the difference is that b
is executed "here".
add a comment |Â
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
21
down vote
accepted
From man 1 bash
:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Such subshells inherit variables from the main shell but they are independent. This means mapfile
in your original command operates on its own myarr
. Then echo
(being outside the pipe) prints empty myarr
(which is the main shell's myarr
).
This command works differently:
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]";
In this case mapfile
and echo
operate on the same myarr
(which is not the main shell's myarr
).
To change the main shell's myarr
you have to run mapfile
in the main shell exactly. Example:
myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "$myarr[@]"
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
add a comment |Â
up vote
21
down vote
accepted
From man 1 bash
:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Such subshells inherit variables from the main shell but they are independent. This means mapfile
in your original command operates on its own myarr
. Then echo
(being outside the pipe) prints empty myarr
(which is the main shell's myarr
).
This command works differently:
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]";
In this case mapfile
and echo
operate on the same myarr
(which is not the main shell's myarr
).
To change the main shell's myarr
you have to run mapfile
in the main shell exactly. Example:
myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "$myarr[@]"
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
add a comment |Â
up vote
21
down vote
accepted
up vote
21
down vote
accepted
From man 1 bash
:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Such subshells inherit variables from the main shell but they are independent. This means mapfile
in your original command operates on its own myarr
. Then echo
(being outside the pipe) prints empty myarr
(which is the main shell's myarr
).
This command works differently:
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]";
In this case mapfile
and echo
operate on the same myarr
(which is not the main shell's myarr
).
To change the main shell's myarr
you have to run mapfile
in the main shell exactly. Example:
myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "$myarr[@]"
From man 1 bash
:
Each command in a pipeline is executed as a separate process (i.e., in a subshell).
Such subshells inherit variables from the main shell but they are independent. This means mapfile
in your original command operates on its own myarr
. Then echo
(being outside the pipe) prints empty myarr
(which is the main shell's myarr
).
This command works differently:
find . -maxdepth 1 -name "mysqldump*" | mapfile -t myarr; echo "$myarr[@]";
In this case mapfile
and echo
operate on the same myarr
(which is not the main shell's myarr
).
To change the main shell's myarr
you have to run mapfile
in the main shell exactly. Example:
myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "$myarr[@]"
edited Aug 14 at 11:32
answered Aug 14 at 11:23
Kamil Maciorowski
17.9k134062
17.9k134062
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
add a comment |Â
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
Added the link to "process substituion" as given in Attie's response, in case a visitor has a TL;DR moment.
â David Tonhofer
Aug 16 at 9:05
add a comment |Â
up vote
10
down vote
Bash runs the commands of a pipeline in a subshell environment, so any variable assignments etc. that take place within it aren't visible to the rest of the shell.
Dash (Debian's /bin/sh
) as well as busybox's sh
are similar, while zsh and ksh
run the last part in the main shell. In Bash, you can use shopt -s lastpipe
to do the same, but it only works when job control is disabled, so not in interactive shells by default.
So:
$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b
(read
and mapfile
have the same issue.)
Alternatively (and as mentioned by Attie), use process substitution, which works like a generalized pipe, and is supported in Bash, ksh and zsh.
$ bash -c 'x=a; read x < <(echo b); echo $x'
b
POSIX leaves it unspecified if the parts of a pipeline run in subshells or not, so it can't really be said that any of the shells would be "wrong" in this.
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
add a comment |Â
up vote
10
down vote
Bash runs the commands of a pipeline in a subshell environment, so any variable assignments etc. that take place within it aren't visible to the rest of the shell.
Dash (Debian's /bin/sh
) as well as busybox's sh
are similar, while zsh and ksh
run the last part in the main shell. In Bash, you can use shopt -s lastpipe
to do the same, but it only works when job control is disabled, so not in interactive shells by default.
So:
$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b
(read
and mapfile
have the same issue.)
Alternatively (and as mentioned by Attie), use process substitution, which works like a generalized pipe, and is supported in Bash, ksh and zsh.
$ bash -c 'x=a; read x < <(echo b); echo $x'
b
POSIX leaves it unspecified if the parts of a pipeline run in subshells or not, so it can't really be said that any of the shells would be "wrong" in this.
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
add a comment |Â
up vote
10
down vote
up vote
10
down vote
Bash runs the commands of a pipeline in a subshell environment, so any variable assignments etc. that take place within it aren't visible to the rest of the shell.
Dash (Debian's /bin/sh
) as well as busybox's sh
are similar, while zsh and ksh
run the last part in the main shell. In Bash, you can use shopt -s lastpipe
to do the same, but it only works when job control is disabled, so not in interactive shells by default.
So:
$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b
(read
and mapfile
have the same issue.)
Alternatively (and as mentioned by Attie), use process substitution, which works like a generalized pipe, and is supported in Bash, ksh and zsh.
$ bash -c 'x=a; read x < <(echo b); echo $x'
b
POSIX leaves it unspecified if the parts of a pipeline run in subshells or not, so it can't really be said that any of the shells would be "wrong" in this.
Bash runs the commands of a pipeline in a subshell environment, so any variable assignments etc. that take place within it aren't visible to the rest of the shell.
Dash (Debian's /bin/sh
) as well as busybox's sh
are similar, while zsh and ksh
run the last part in the main shell. In Bash, you can use shopt -s lastpipe
to do the same, but it only works when job control is disabled, so not in interactive shells by default.
So:
$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b
(read
and mapfile
have the same issue.)
Alternatively (and as mentioned by Attie), use process substitution, which works like a generalized pipe, and is supported in Bash, ksh and zsh.
$ bash -c 'x=a; read x < <(echo b); echo $x'
b
POSIX leaves it unspecified if the parts of a pipeline run in subshells or not, so it can't really be said that any of the shells would be "wrong" in this.
edited Aug 15 at 8:13
answered Aug 14 at 12:39
ilkkachu
551212
551212
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
add a comment |Â
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
1
1
If you disable bash's job control you can use lastpipe in an interactive shell, too :
set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
If you disable bash's job control you can use lastpipe in an interactive shell, too :
set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
â Cyrus
Aug 15 at 5:36
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
@Cyrus, ah right, I'd forgotten the details, thanks
â ilkkachu
Aug 15 at 8:17
add a comment |Â
up vote
8
down vote
As Kamil has pointed out, each element in the pipeline is a separate process.
You can use the following process substitution to get find
to run in a different process, with the mapfile
invocation remaining in your current interpreter, allowing access to myarr
afterwards:
myarr=()
mapfile -t myarr < <( find . -maxdepth 1 -name "mysqldump*" )
echo "$myarr[@]"
b < <( a )
will act similarly to a | b
in terms of how the pipeline is wired - the difference is that b
is executed "here".
add a comment |Â
up vote
8
down vote
As Kamil has pointed out, each element in the pipeline is a separate process.
You can use the following process substitution to get find
to run in a different process, with the mapfile
invocation remaining in your current interpreter, allowing access to myarr
afterwards:
myarr=()
mapfile -t myarr < <( find . -maxdepth 1 -name "mysqldump*" )
echo "$myarr[@]"
b < <( a )
will act similarly to a | b
in terms of how the pipeline is wired - the difference is that b
is executed "here".
add a comment |Â
up vote
8
down vote
up vote
8
down vote
As Kamil has pointed out, each element in the pipeline is a separate process.
You can use the following process substitution to get find
to run in a different process, with the mapfile
invocation remaining in your current interpreter, allowing access to myarr
afterwards:
myarr=()
mapfile -t myarr < <( find . -maxdepth 1 -name "mysqldump*" )
echo "$myarr[@]"
b < <( a )
will act similarly to a | b
in terms of how the pipeline is wired - the difference is that b
is executed "here".
As Kamil has pointed out, each element in the pipeline is a separate process.
You can use the following process substitution to get find
to run in a different process, with the mapfile
invocation remaining in your current interpreter, allowing access to myarr
afterwards:
myarr=()
mapfile -t myarr < <( find . -maxdepth 1 -name "mysqldump*" )
echo "$myarr[@]"
b < <( a )
will act similarly to a | b
in terms of how the pipeline is wired - the difference is that b
is executed "here".
answered Aug 14 at 11:31
Attie
8,30731934
8,30731934
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsuperuser.com%2fquestions%2f1348948%2fcant-pipe-in-bashs-mapfile-but-why%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
5
Similar on unix.SE: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?
â ilkkachu
Aug 14 at 12:30
Excellent answers all around, thank you everyone. Interesting how the execution strategy of the pipeline (each part running in a spearate process) leaks "upwards" and modifies the apparent meaning of the code, basically silently putting "local" in front of every variable appearing in the pipe. In a language that is something other than crazy glue for other programs, that would be bug, hopefully.
â David Tonhofer
Aug 15 at 7:30
2
If you give the code to shellcheck, you get warnings: SC2030: "Modification of var is local (to subshell caused by pipeline)" and SC2031: "var was modified in a subshell. That change might be lost.". Excellent.
â David Tonhofer
Aug 15 at 7:31
Why using
find
andmapfile
here at all and not just simplymyarr=(mysqldump*)
? This will even work with filennames with spaces and newlines.â BlackJack
Aug 15 at 14:43
1
Just noticed that one has to turn
nullglob
option on (shopt -s nullglob
) on formyarr=(mysqldump*)
to not end up with the array('mysqldump*')
in case no files match.â David Tonhofer
Aug 16 at 8:52