Search for text in all files and if matched move files to different folder

  • Hi All,

    I have just started using powershell and came across a problem:

    I have to search for a particluar word/s in all the files of a folder and if a word is matched that file should be moved to a different folder.

    I have tried this

    get-ChildItem -include *.txt -recurse | select-String -pattern "Msg","Level"

    and it gives me the correct output

    test.sql.txt:1:Msg 156, Level 15, State 1, Server Line 2

    test1.sql.txt:1:Msg 102, Level 15, State 1, Server Line 1

    Now I want the above matched files moved to a different folder. How can I do this ?

    Also, can I use Powershell to email those files to me ? I know this can be done, but need some help !

    Thanks,

    \\K

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • You'd use Move-Item to move the files. Like this:

    get-ChildItem -include *.txt -recurse | select-String -pattern "Msg","Level"|Move-Item -destination [path]

    I don't know how to email the files in PowerShell, but I'm sure you can do it.

  • Jack Corbett (12/11/2009)


    You'd use Move-Item to move the files. Like this:

    get-ChildItem -include *.txt -recurse | select-String -pattern "Msg","Level"|Move-Item -destination [path]

    I don't know how to email the files in PowerShell, but I'm sure you can do it.

    Jack .... Thanks for your reply !

    This dosen't work! It does not move the files found ! Before posting the topic, I tried the same, but it does not work.

    I think I need to use a variable and then use foreach loop, but dont know how to do this !

    I refered to http://www.visualbasicscript.com/Using-Powershell-to-Check-Files-m53557.aspx as reference.

    Is there any other way of doing that ?

    Need help !

    Thanks,

    \\K

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • Sorry but I only have 30 seconds...

    The first part is selecting all .txt files (output: file objects).

    The second part is selecting each line of text that matches the pattern (output: strings).

    The third part is then trying to move these lines of text as if they were files.

    I think that you need more than a one liner (it is possible but does that mean you should do it?)

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Gary Istvan Varga (12/11/2009)


    Sorry but I only have 30 seconds...

    The first part is selecting all .txt files (output: file objects).

    The second part is selecting each line of text that matches the pattern (output: strings).

    The third part is then trying to move these lines of text as if they were files.

    Yes Gary, that's what I want and I have managed to do till second part. I need some direction for third part. Like what to use to move those files as Move-Item is not doing that.

    I think that you need more than a one liner (it is possible but does that mean you should do it?)

    I am a novice in using powershell, as writing codes is what I have starting to learn. 🙂

    Thanks for your help,

    \\K

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • My point is that you cannot take the output from the second part as an input to the third as you are then trying to treat the message lines as they were files.

    I have no time to check this but you could do something like the following (there are better ways):

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = select-String -pattern "Msg","Level"

    if (0 < $messages.Length)

    {

    Move-Item -path $file.FullName -destination [path]

    }

    }

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Gary Istvan Varga (12/11/2009)


    My point is that you cannot take the output from the second part as an input to the third as you are then trying to treat the message lines as they were files.

    I have no time to check this but you could do something like the following (there are better ways):

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = select-String -pattern "Msg","Level"

    if (0 < $messages.Length)

    {

    Move-Item -path $file.FullName -destination [path]

    }

    }

    Thanks so much Gary,

    I was trying this to work for days before posting this topic. I modified your hint in case you want to have a look

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem path -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = $file | select-String -pattern "Msg","Level" |get-ChildItem -name

    if (0 -lt $messages.Length )

    {

    Move-Item -path $file.FullName -destination path

    }

    }

    I changed the comparision operator and the $file variable to pipe to the string match and it worked magic ! 🙂

    One more question: Can I email the files as attachment ? can you point some direction ?

    Thanks for your time and help,

    \\K

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • First off, can I just say thanks to \\K for posting back the real solution (and confirmation that that part works) as this is so much better for the people who come up against the same problem in the future and come across this thread. It is one of my plethora of pet hates when people post a "I've sorted it now." response without saying what the applied resolution was.

    Emailing to follow.

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • See Jeffrey Snover comment to this question: http://www.searchmarked.com/windows/how-to-send-an-email-using-a-windows-powershell-script.php

    FYI Jeffrey Snover is the MS guy for PowerShell. No Jeffrey Snover, no PowerShell (some of his colleagues will have done a little more than drink cola and eat pizza as well). As an aside, he is a really nice chap to boot!!!

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Gary Istvan Varga (12/13/2009)


    See Jeffrey Snover comment to this question: http://www.searchmarked.com/windows/how-to-send-an-email-using-a-windows-powershell-script.php

    FYI Jeffrey Snover is the MS guy for PowerShell. No Jeffrey Snover, no PowerShell (some of his colleagues will have done a little more than drink cola and eat pizza as well). As an aside, he is a really nice chap to boot!!!

    Thanks for the link Gary !

    That works perfect for email.

    One last question. how can I modify the script to move files so that I can run that script and the email script as a whole ?

    The reason I am telling is that I want to use the variable $messages as an input to the email script.

    The variable $messages --> stores the filenames. So that can be used to send them as attachments.

    Below is my script that I have tried so far. It sends email, but without attachment :hehe:

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem \\servername\foldername\\OutputLogs\ -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = $file | select-String -pattern "Msg","Level" |get-ChildItem -name

    if (0 -lt $messages.Length )

    {

    Move-Item -path $file.FullName -destination "\\servername\foldername\\OutputLogs\Emailed"

    }

    }

    # Send email

    $file = $messages

    $smtpServer = "SMTP Server Name"

    $msg = new-object Net.Mail.MailMessage

    $att = new-object Net.Mail.Attachment($file)

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)

    $msg.From = "test@domain.com"

    $msg.To.Add("test@domain.com")

    $msg.Subject = "Test email from scriptrunner-sdb10"

    $msg.Body = "This email is send uinsg powershell"

    $msg.Attachments.Add($att)

    $smtp.Send($msg)

    $att.Dispose() #to close the attached file correctly

    Thank you again for your time,

    \\K 🙂

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • Remember that $messages is an array of strings comprised of absolute path filenames. What you are trying to do is add an array as an attachment. You want to do one of the following:

    1) send an email for each file:

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem \\servername\foldername\\OutputLogs\ -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = $file | select-String -pattern "Msg","Level" |get-ChildItem -name

    if (0 -lt $messages.Length )

    {

    Move-Item -path $file.FullName -destination "\\servername\foldername\\OutputLogs\Emailed"

    }

    }

    # Send email for each selected text file in turn

    Foreach ($filename in $messages)

    {

    $smtpServer = "SMTP Server Name"

    $msg = new-object Net.Mail.MailMessage

    $att = new-object Net.Mailn.Attachment($filename)

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)

    $msg.From = "test@domain.com"

    $msg.To.Add("test@domain.com")

    $msg.Subject = "Test email from scriptrunner-sdb10"

    $msg.Body = "This email is send uinsg powershell"

    $msg.Attachments.Add($att)

    $smtp.Send($msg)

    $att.Dispose() #to close the attached file correctly

    }

    2) send one email with each file attached:

    # Deal with each text file in turn

    Foreach ($file in get-ChildItem \\servername\foldername\\OutputLogs\ -include *.txt -recurse)

    {

    # Assuming it is an array returned

    $messages = $file | select-String -pattern "Msg","Level" |get-ChildItem -name

    if (0 -lt $messages.Length )

    {

    Move-Item -path $file.FullName -destination "\\servername\foldername\\OutputLogs\Emailed"

    }

    }

    # Send email

    $smtpServer = "SMTP Server Name"

    $msg = new-object Net.Mail.MailMessage

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)

    $msg.From = "test@domain.com"

    $msg.To.Add("test@domain.com")

    $msg.Subject = "Test email from scriptrunner-sdb10"

    $msg.Body = "This email is send uinsg powershell"

    $attachments = new-object Net.Mail.Attachment[] $messages.Length

    Foreach ($count = 0; $count -lt $messages.Length; $count++)

    {

    $attachments[count] = new-object Net.Mail.Attachment($messages[count])

    $msg.Attachments.Add($attachments[count])

    # Can do following after loop? $msg.Attachments.Add($attachments)

    }

    $smtp.Send($msg)

    Foreach ($attachment in $attachments)

    {

    $attachment.Dispose() #to close the attached file correctly

    }

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Gary,

    I am using your 2nd code as that will send less emails to the team with all files attached, but I am getting following error:

    Missing 'in' after variable in foreach loop. file.ps1:24 char:17 + Foreach ($count - <<<< eq 0; $count -lt $messages.Length; $count++)

    I checked the syntax and what you have mentioned in 2 is all correct ! I dont know why it is complaining about "in" ?

    Any ideas :w00t:

    Edit ***

    I tried comenting out some stuff like below to figure out where the problem is :

    # Send email

    $smtpServer = "SMTPServerName"

    $msg = new-object Net.Mail.MailMessage

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)

    $msg.From = "test@domain.com"

    $msg.To.Add("test@domain.com")

    $msg.Subject = "Test email from database"

    $msg.Body = "This email is send uinsg powershell"

    $attachments = new-object Net.Mail.Attachment[] $messages.Length

    #Foreach ($count -eq 0; $count -lt $messages.Length; $count++)

    #{

    # $attachments[count] = new-object Net.Mail.Attachment($messages[count])

    # $msg.Attachments.Add($attachments[count])

    # break

    # # Can do following after loop? $msg.Attachments.Add($attachments)

    #}

    $smtp.Send($msg)

    #Foreach ($attachment in $attachments)

    #{

    # $attachment.Dispose() #to close the attached file correctly

    #}

    This gives me an error

    New-Object : Constructor not found. Cannot find an appropriate constructor for

    type Net.Mail.Attachment[].

    At S:\emailtest.ps1:10 char:26

    + $attachments = new-object <<<< Net.Mail.Attachment[] $messages.Length

    Also, using your first script ,the email is sent but no attachment and on powershell screen, I am getting following errors:

    New-Object : Constructor not found. Cannot find an appropriate constructor for

    type Net.Mail.Attachment.

    At S:\emailtest.ps1:20 char:21

    + $att = new-object <<<< Net.Mail.Attachment($filename)

    Exception calling "Add" with "1" argument(s): "Value cannot be null.

    Parameter name: item"

    At S:\emailtest.ps1:27 char:24

    + $msg.Attachments.Add( <<<< $att)

    You cannot call a method on a null-valued expression.

    At S:\emailtest.ps1:29 char:16

    + $att.Dispose( <<<< ) #to close the attached file correctly

    Edit *******

    I have used the below code to send email with attachments, but it sends all the files present in the folder .... I want to send only the files that are moved to the folder

    $smtpServer = "SMTP Server Name"

    $msg = new-object Net.Mail.MailMessage

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)

    $msg.From = "test@domain.com"

    $msg.To.Add("test@domain.com")

    $msg.Subject = "Email with attachment"

    $msg.Body = "Powershell sending email with files attached"

    foreach ($file in gci "\\servername\OutputLogs\Emailed"){

    $att = New-Object Net.Mail.Attachment($file.fullname)

    $msg.Attachments.Add($att)

    }

    $smtp.Send($msg)

    I know I am missing something ... but cant figure out :hehe:

    Thanks,

    \\K

    ______________________________________________________________________________________________________________________________________________________________________________________
    HTH !
    Kin
    MCTS : 2005, 2008
    Active SQL Server Community Contributor 🙂

  • Sorry. In my haste I have been a complete and utter Muppet.

    When I wrote:

    Foreach ($count -eq 0; $count -lt $messages.Length; $count++)

    it should have been:

    For ($count -eq 0; $count -lt $messages.Length; $count++)

    It is a For loop not a ForEach loop.

    For the lawyers: Muppet is a trandmark of The Jim Henson Company.

    For my friends: acting like a Muppet is a trademark of Gary Varga.

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Removed duplicate post (browser issue).

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

  • Next...

    I was being silly with arrays (been a while since I've done serious PowerShell).

    Instead of

    $attachments = new-object Net.Mail.Attachment[] $messages.Length

    and

    $attachments[count] = new-object Net.Mail.Attachment($messages[count])

    $msg.Attachments.Add($attachments[count])

    try

    $attachments = @()

    and

    $attachment = new-object Net.Mail.Attachment($messages[count])

    $attachments = $attachments + $attachment

    $msg.Attachments.Add($attachment)

    or even better by replacing

    Foreach ($count = 0; $count -lt $messages.Length; $count++)

    {

    $attachments[count] = new-object Net.Mail.Attachment($messages[count])

    $msg.Attachments.Add($attachments[count])

    # Can do following after loop? $msg.Attachments.Add($attachments)

    }

    with

    Foreach ($message in $messages)

    {

    $attachment = new-object Net.Mail.Attachment($message)

    $attachments = $attachments + $attachment

    $msg.Attachments.Add($attachment)

    # Can do following after loop? $msg.Attachments.Add($attachments)

    }

    Gaz

    -- Stop your grinnin' and drop your linen...they're everywhere!!!

Viewing 15 posts - 1 through 15 (of 19 total)

You must be logged in to reply to this topic. Login to reply